Repository: android/platform_build Branch: main Commit: 045a3d6a3e35 Files: 1380 Total size: 7.0 MB Directory structure: gitextract_4wb4gfr7/ ├── .gitignore ├── Android.bp ├── Changes.md ├── CleanSpec.mk ├── Deprecation.md ├── OWNERS ├── PREUPLOAD.cfg ├── README.md ├── Usage.txt ├── backported_fixes/ │ ├── Android.bp │ ├── OWNERS │ ├── applied_fixes/ │ │ └── ki350037023.txtpb │ ├── backported_fixes.proto │ ├── src/ │ │ └── java/ │ │ └── com/ │ │ └── android/ │ │ └── build/ │ │ └── backportedfixes/ │ │ ├── CombineBackportedFixes.java │ │ ├── WriteBackportedFixesPropFile.java │ │ └── common/ │ │ └── Parser.java │ └── tests/ │ └── java/ │ └── com/ │ └── android/ │ └── build/ │ └── backportedfixes/ │ ├── CombineBackportedFixesTest.java │ ├── WriteBackportedFixesPropFileTest.java │ └── common/ │ └── ParserTest.java ├── banchanHelp.sh ├── buildspec.mk.default ├── ci/ │ ├── Android.bp │ ├── AndroidTest.xml.template │ ├── build_context.py │ ├── build_device_and_tests │ ├── build_metadata │ ├── build_test_suites │ ├── build_test_suites.py │ ├── build_test_suites_local_test.py │ ├── build_test_suites_test.py │ ├── buildbot.py │ ├── ci_test_lib.py │ ├── dump_product_config │ ├── metrics_agent.py │ ├── optimized_targets.py │ ├── optimized_targets_test.py │ ├── test_discovery_agent.py │ └── test_mapping_module_retriever.py ├── common/ │ ├── core.mk │ ├── json.mk │ ├── math.mk │ └── strings.mk ├── core/ │ ├── LINUX_KERNEL_COPYING │ ├── Makefile │ ├── OWNERS │ ├── WINPTHREADS_COPYING │ ├── aapt2.mk │ ├── aapt_flags.mk │ ├── allowed_ndk_types.mk │ ├── android_manifest.mk │ ├── android_soong_config_vars.mk │ ├── app_certificate_validate.mk │ ├── app_prebuilt_internal.mk │ ├── art_config.mk │ ├── artifact_path_requirements.mk │ ├── autogen_test_config.mk │ ├── base_rules.mk │ ├── binary.mk │ ├── board_config.mk │ ├── board_config_wifi.mk │ ├── board_config_wpa_supplicant.mk │ ├── build-system.html │ ├── build_id.mk │ ├── build_rro_package.mk │ ├── cc_prebuilt_internal.mk │ ├── ccache.mk │ ├── check_elf_file.mk │ ├── checktree │ ├── clang/ │ │ ├── HOST_x86.mk │ │ ├── HOST_x86_64.mk │ │ ├── TARGET_arm.mk │ │ ├── TARGET_arm64.mk │ │ ├── TARGET_riscv64.mk │ │ ├── TARGET_x86.mk │ │ ├── TARGET_x86_64.mk │ │ ├── config.mk │ │ └── tidy.mk │ ├── cleanbuild.mk │ ├── cleanspec.mk │ ├── clear_vars.mk │ ├── combo/ │ │ ├── HOST_darwin.mk │ │ ├── HOST_linux.mk │ │ ├── TARGET_linux-arm.mk │ │ ├── TARGET_linux-arm64.mk │ │ ├── TARGET_linux-riscv64.mk │ │ ├── TARGET_linux-x86.mk │ │ ├── TARGET_linux-x86_64.mk │ │ ├── javac.mk │ │ └── select.mk │ ├── config.mk │ ├── config_sanitizers.mk │ ├── configure_module_stem.mk │ ├── copy_headers.mk │ ├── cxx_stl_setup.mk │ ├── definitions.mk │ ├── deprecation.mk │ ├── dex_preopt.mk │ ├── dex_preopt_config.mk │ ├── dex_preopt_config_merger.py │ ├── dex_preopt_odex_install.mk │ ├── distdir.mk │ ├── dumpconfig.mk │ ├── dumpvar.mk │ ├── dupcheck.sh │ ├── dynamic_binary.mk │ ├── empty_test_config.xml │ ├── envsetup.mk │ ├── executable.mk │ ├── executable_internal.mk │ ├── executable_prefer_symlink.mk │ ├── filter_symbols.sh │ ├── force_aapt2.mk │ ├── fuzz_test.mk │ ├── generate_enforce_rro.mk │ ├── header_library.mk │ ├── header_library_internal.mk │ ├── host_executable.mk │ ├── host_executable_internal.mk │ ├── host_java_library.mk │ ├── host_java_library_common.mk │ ├── host_prebuilt.mk │ ├── host_shared_library.mk │ ├── host_shared_library_internal.mk │ ├── host_static_library.mk │ ├── host_static_library_internal.mk │ ├── install_jni_libs.mk │ ├── install_jni_libs_internal.mk │ ├── instrumentation_test_config_template.xml │ ├── jacoco.mk │ ├── java.mk │ ├── java_common.mk │ ├── java_host_test_config_template.xml │ ├── java_host_unit_test_config_template.xml │ ├── java_library.mk │ ├── java_prebuilt_internal.mk │ ├── java_renderscript.mk │ ├── java_test_config_template.xml │ ├── layoutlib_data.mk │ ├── link_type.mk │ ├── local_current_sdk.mk │ ├── local_systemsdk.mk │ ├── local_vendor_product.mk │ ├── main.mk │ ├── misc_prebuilt_internal.mk │ ├── module_arch_supported.mk │ ├── multi_prebuilt.mk │ ├── multilib.mk │ ├── native_benchmark_test_config_template.xml │ ├── native_host_test_config_template.xml │ ├── native_test.mk │ ├── native_test_config_template.xml │ ├── ninja_config.mk │ ├── node_fns.mk │ ├── notice_files.mk │ ├── os_licensing.mk │ ├── pack_dyn_relocs_setup.mk │ ├── package.mk │ ├── package_internal.mk │ ├── packaging/ │ │ └── flags.mk │ ├── pathmap.mk │ ├── phony_package.mk │ ├── prebuilt.mk │ ├── prebuilt_internal.mk │ ├── process_wrapper.sh │ ├── process_wrapper_gdb.cmds │ ├── process_wrapper_gdb.sh │ ├── product-graph.mk │ ├── product.mk │ ├── product_config.mk │ ├── product_config.rbc │ ├── product_validation_checks.mk │ ├── proguard/ │ │ ├── checknotnull.flags │ │ └── kotlin.flags │ ├── proguard.flags │ ├── proguard.jacoco.flags │ ├── proguard_basic_keeps.flags │ ├── project_definitions.mk │ ├── python_binary_host_mobly_test_config_template.xml │ ├── python_binary_host_test_config_template.xml │ ├── ravenwood_test_config_template.xml │ ├── rbe.mk │ ├── release_config.mk │ ├── release_config.scl │ ├── robolectric_test_config_template.xml │ ├── root.mk │ ├── rust_device_benchmark_config_template.xml │ ├── rust_device_test_config_template.xml │ ├── rust_host_benchmark_config_template.xml │ ├── rust_host_test_config_template.xml │ ├── sbom.mk │ ├── sdk_check.mk │ ├── shared_library.mk │ ├── shared_library_internal.mk │ ├── shell_test_config_template.xml │ ├── soong_android_app_set.mk │ ├── soong_app_prebuilt.mk │ ├── soong_cc_rust_prebuilt.mk │ ├── soong_config.mk │ ├── soong_droiddoc_prebuilt.mk │ ├── soong_extra_config.mk │ ├── soong_java_prebuilt.mk │ ├── static_java_library.mk │ ├── static_library.mk │ ├── static_library_internal.mk │ ├── suite_host_config.mk │ ├── support_libraries.mk │ ├── sysprop.mk │ ├── sysprop_config.mk │ ├── target_test_internal.mk │ ├── tasks/ │ │ ├── README.dex_preopt_check.md │ │ ├── art-host-tests.mk │ │ ├── art.mk │ │ ├── automotive-general-tests.mk │ │ ├── automotive-sdv-tests.mk │ │ ├── automotive-tests.mk │ │ ├── autorepro.mk │ │ ├── berberis_test.mk │ │ ├── build_custom_images.mk │ │ ├── catbox.mk │ │ ├── check-abi-dump-list.mk │ │ ├── csuite.mk │ │ ├── cts.mk │ │ ├── cts_root.mk │ │ ├── device-platinum-tests.mk │ │ ├── device-tests.mk │ │ ├── dex_preopt_check.mk │ │ ├── dts.mk │ │ ├── find-shareduid-violation.mk │ │ ├── fontchain_lint.mk │ │ ├── general-tests.mk │ │ ├── host-unit-tests.mk │ │ ├── host_init_verifier.mk │ │ ├── mcts.mk │ │ ├── meta-lic.mk │ │ ├── module-info.mk │ │ ├── mts.mk │ │ ├── multitree.mk │ │ ├── oem_image.mk │ │ ├── offline-sdk-docs.mk │ │ ├── owners.mk │ │ ├── performance-tests.mk │ │ ├── platform_availability_check.mk │ │ ├── prebuilt_tradefed.mk │ │ ├── sdk-addon.mk │ │ ├── sts.mk │ │ ├── tools/ │ │ │ ├── build_custom_image.mk │ │ │ ├── compatibility.mk │ │ │ └── package-modules.mk │ │ ├── tradefed-tests-list.mk │ │ ├── vendor_module_check.mk │ │ ├── vts-core-tests.mk │ │ ├── with-license.mk │ │ └── wvts.mk │ ├── use_lld_setup.mk │ └── version_util.mk ├── envsetup.sh ├── help.sh ├── navbar.md ├── packaging/ │ ├── distdir.mk │ ├── main.mk │ └── main_soong_only.mk ├── rbesetup.sh ├── shell_utils.sh ├── tapasHelp.sh ├── target/ │ ├── board/ │ │ ├── BoardConfigGsiCommon.mk │ │ ├── BoardConfigMainlineCommon.mk │ │ ├── BoardConfigPixelCommon.mk │ │ ├── android-info.mk │ │ ├── generic/ │ │ │ ├── AndroidBoard.mk │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── generic_64bitonly_x86_64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system.prop │ │ ├── generic_arm64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ ├── sepolicy/ │ │ │ │ ├── OWNERS │ │ │ │ ├── file.te │ │ │ │ └── file_contexts │ │ │ └── system_ext.prop │ │ ├── generic_arm64_plus_armv7/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ └── device.mk │ │ ├── generic_riscv64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── generic_x86/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── generic_x86_64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── generic_x86_64_arm64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── generic_x86_arm/ │ │ │ ├── BoardConfig.mk │ │ │ ├── README.txt │ │ │ ├── device.mk │ │ │ └── system_ext.prop │ │ ├── go_defaults.prop │ │ ├── go_defaults_512.prop │ │ ├── go_defaults_common.prop │ │ ├── gsi_arm64/ │ │ │ └── BoardConfig.mk │ │ ├── gsi_system_ext.prop │ │ ├── gsi_system_ext_user.prop │ │ ├── linux_bionic/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── mainline_arm64/ │ │ │ ├── BoardConfig.mk │ │ │ ├── bluetooth/ │ │ │ │ └── bdroid_buildcfg.h │ │ │ └── sepolicy/ │ │ │ ├── OWNERS │ │ │ ├── file.te │ │ │ └── file_contexts │ │ ├── mainline_sdk/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── mainline_x86/ │ │ │ └── BoardConfig.mk │ │ ├── mainline_x86_64/ │ │ │ └── BoardConfig.mk │ │ ├── mainline_x86_arm/ │ │ │ └── BoardConfig.mk │ │ ├── module_arm/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_arm64/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_arm64only/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_riscv64/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_x86/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_x86_64/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ ├── module_x86_64only/ │ │ │ ├── BoardConfig.mk │ │ │ └── README.md │ │ └── ndk/ │ │ ├── BoardConfig.mk │ │ └── README.md │ └── product/ │ ├── AndroidProducts.mk │ ├── OWNERS │ ├── angle_default.mk │ ├── aosp_64bitonly_x86_64.mk │ ├── aosp_arm.mk │ ├── aosp_arm64.mk │ ├── aosp_arm64_fullmte.mk │ ├── aosp_arm64_plus_armv7.mk │ ├── aosp_base.mk │ ├── aosp_base_telephony.mk │ ├── aosp_product.mk │ ├── aosp_riscv64.mk │ ├── aosp_x86.mk │ ├── aosp_x86_64.mk │ ├── aosp_x86_arm.mk │ ├── app_function_extensions.mk │ ├── base.mk │ ├── base_product.mk │ ├── base_system.mk │ ├── base_system_ext.mk │ ├── base_vendor.mk │ ├── build_variables.mk │ ├── cfi-common.mk │ ├── core_64_bit.mk │ ├── core_64_bit_only.mk │ ├── core_minimal.mk │ ├── core_no_zygote.mk │ ├── default_art_config.mk │ ├── developer_gsi_keys.mk │ ├── empty-preloaded-classes │ ├── empty-profile │ ├── emulated_storage.mk │ ├── full.manifest.xml │ ├── full.mk │ ├── full_base.mk │ ├── full_base_telephony.mk │ ├── full_x86.mk │ ├── fullmte.mk │ ├── generic/ │ │ ├── Android.bp │ │ ├── OWNERS │ │ └── erofs_compress_hints.txt │ ├── generic.mk │ ├── generic_no_telephony.mk │ ├── generic_ramdisk.mk │ ├── generic_system.mk │ ├── generic_system_arm64.mk │ ├── generic_system_x86.mk │ ├── generic_system_x86_64.mk │ ├── generic_system_x86_arm.mk │ ├── generic_x86.mk │ ├── go_defaults.mk │ ├── go_defaults_512.mk │ ├── go_defaults_common.mk │ ├── gsi/ │ │ ├── 28.txt │ │ ├── 29.txt │ │ ├── 30.txt │ │ ├── 31.txt │ │ ├── 32.txt │ │ ├── 33.txt │ │ ├── 34.txt │ │ ├── Android.bp │ │ ├── OWNERS │ │ ├── current.txt │ │ ├── gsi_skip_mount.cfg │ │ ├── init.gsi.rc │ │ ├── init.vndk-nodef.rc │ │ └── testkey_rsa2048.pem │ ├── gsi_release.mk │ ├── handheld_product.mk │ ├── handheld_system.mk │ ├── handheld_system_ext.mk │ ├── handheld_vendor.mk │ ├── hsum_common.mk │ ├── languages_default.mk │ ├── languages_full.mk │ ├── large_screen_common.mk │ ├── linux_bionic.mk │ ├── mainline_sdk.mk │ ├── mainline_system_arm64.mk │ ├── mainline_system_x86.mk │ ├── mainline_system_x86_64.mk │ ├── mainline_system_x86_arm.mk │ ├── media_product.mk │ ├── media_system.mk │ ├── media_system_ext.mk │ ├── media_vendor.mk │ ├── memtag-common.mk │ ├── module_arm.mk │ ├── module_arm64.mk │ ├── module_arm64only.mk │ ├── module_common.mk │ ├── module_riscv64.mk │ ├── module_x86.mk │ ├── module_x86_64.mk │ ├── module_x86_64only.mk │ ├── ndk.mk │ ├── non_ab_device.mk │ ├── product_launched_with_k.mk │ ├── product_launched_with_l.mk │ ├── product_launched_with_l_mr1.mk │ ├── product_launched_with_m.mk │ ├── product_launched_with_n.mk │ ├── product_launched_with_n_mr1.mk │ ├── product_launched_with_o.mk │ ├── product_launched_with_o_mr1.mk │ ├── product_launched_with_p.mk │ ├── profile_boot_common.mk │ ├── ramdisk_stub.mk │ ├── runtime_libart.mk │ ├── sdk.mk │ ├── sdk_with_runtime_apis.mk │ ├── security/ │ │ ├── Android.bp │ │ ├── README │ │ ├── bluetooth.pk8 │ │ ├── bluetooth.x509.pem │ │ ├── cts_uicc_2021.pk8 │ │ ├── cts_uicc_2021.x509.pem │ │ ├── fsverity-release.x509.der │ │ ├── media.pk8 │ │ ├── media.x509.pem │ │ ├── networkstack.pk8 │ │ ├── networkstack.x509.pem │ │ ├── nfc.pk8 │ │ ├── nfc.x509.pem │ │ ├── platform.pk8 │ │ ├── platform.x509.pem │ │ ├── sdk_sandbox.pk8 │ │ ├── sdk_sandbox.x509.pem │ │ ├── shared.pk8 │ │ ├── shared.x509.pem │ │ ├── testkey.pk8 │ │ └── testkey.x509.pem │ ├── sysconfig/ │ │ ├── Android.bp │ │ ├── initial-package-stopped-states-aosp.xml │ │ ├── preinstalled-packages-platform-aosp-product.xml │ │ ├── preinstalled-packages-platform-full-base.xml │ │ ├── preinstalled-packages-platform-generic-system.xml │ │ ├── preinstalled-packages-platform-handheld-product.xml │ │ ├── preinstalled-packages-platform-handheld-system.xml │ │ └── preinstalled-packages-platform-telephony-product.xml │ ├── telephony.mk │ ├── telephony_product.mk │ ├── telephony_system.mk │ ├── telephony_system_ext.mk │ ├── telephony_vendor.mk │ ├── updatable_apex.mk │ ├── userspace_reboot.mk │ ├── virtual_ab_ota/ │ │ ├── OWNERS │ │ ├── README.md │ │ ├── android_t_baseline.mk │ │ ├── compression.mk │ │ ├── compression_retrofit.mk │ │ ├── compression_with_xor.mk │ │ ├── launch.mk │ │ ├── launch_with_vendor_ramdisk.mk │ │ ├── plus_non_ab.mk │ │ ├── retrofit.mk │ │ └── vabc_features.mk │ ├── window_extensions.mk │ └── window_extensions_base.mk ├── teams/ │ ├── Android.bp │ └── OWNERS ├── tests/ │ ├── artifact_path_requirements/ │ │ ├── inherit1.rbc │ │ ├── inherit2.rbc │ │ ├── inherit3.rbc │ │ ├── inherit4.rbc │ │ ├── product.rbc │ │ └── test.rbc │ ├── b_tests.sh │ ├── board.rbc │ ├── board_input_vars.rbc │ ├── conversion_error.rbc │ ├── envsetup_tests.sh │ ├── include1.rbc │ ├── inherits_in_regular_variables/ │ │ ├── inherit1.rbc │ │ ├── product.rbc │ │ └── test.rbc │ ├── input_variables.rbc │ ├── lunch_tests.sh │ ├── part1.rbc │ ├── prefixed_sort_order/ │ │ ├── base-secondary.rbc │ │ ├── base.rbc │ │ ├── product.rbc │ │ └── test.rbc │ ├── product.rbc │ ├── roboleaf_tests.sh │ ├── run.rbc │ ├── single_value_inheritance/ │ │ ├── inherit1.rbc │ │ ├── inherit2.rbc │ │ ├── product.rbc │ │ └── test.rbc │ ├── single_value_inheritance_2/ │ │ ├── a.rbc │ │ ├── b.rbc │ │ ├── c.rbc │ │ ├── d.rbc │ │ ├── product.rbc │ │ └── test.rbc │ └── version_defaults.rbc └── tools/ ├── Android.bp ├── aconfig/ │ ├── .editorconfig │ ├── .gitignore │ ├── Cargo.toml │ ├── MODULE_LICENSE_APACHE2 │ ├── OWNERS │ ├── PREUPLOAD.cfg │ ├── TEST_MAPPING │ ├── aconfig/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── src/ │ │ │ ├── codegen/ │ │ │ │ ├── cpp.rs │ │ │ │ ├── java.rs │ │ │ │ ├── mod.rs │ │ │ │ └── rust.rs │ │ │ ├── commands.rs │ │ │ ├── dump.rs │ │ │ ├── main.rs │ │ │ ├── storage/ │ │ │ │ ├── flag_info.rs │ │ │ │ ├── flag_table.rs │ │ │ │ ├── flag_value.rs │ │ │ │ ├── mod.rs │ │ │ │ └── package_table.rs │ │ │ └── test.rs │ │ ├── templates/ │ │ │ ├── CustomFeatureFlags.java.template │ │ │ ├── ExportedFlags.java.template │ │ │ ├── FakeFeatureFlagsImpl.java.template │ │ │ ├── FeatureFlags.java.template │ │ │ ├── FeatureFlagsImpl.deviceConfig.java.template │ │ │ ├── FeatureFlagsImpl.exported.java.template │ │ │ ├── FeatureFlagsImpl.new_storage.java.template │ │ │ ├── FeatureFlagsImpl.test_mode.java.template │ │ │ ├── Flags.java.template │ │ │ ├── cpp_exported_header.template │ │ │ ├── cpp_source_file.template │ │ │ ├── rust.template │ │ │ └── rust_test.template │ │ └── tests/ │ │ ├── AconfigHostTest.java │ │ ├── AconfigTest.java │ │ ├── AndroidManifest.xml │ │ ├── aconfig_exported_mode_test.cpp │ │ ├── aconfig_exported_mode_test.rs │ │ ├── aconfig_force_read_only_mode_test.cpp │ │ ├── aconfig_force_read_only_mode_test.rs │ │ ├── aconfig_prod_mode_test.rs │ │ ├── aconfig_test.cpp │ │ ├── aconfig_test_mode_test.rs │ │ ├── aconfig_test_test_variant.cpp │ │ ├── first.values │ │ ├── read_only_test.aconfig │ │ ├── read_only_test.values │ │ ├── second.values │ │ ├── storage_test_1.aconfig │ │ ├── storage_test_1.values │ │ ├── storage_test_2.aconfig │ │ ├── storage_test_2.values │ │ ├── storage_test_4.aconfig │ │ ├── storage_test_4.values │ │ ├── test.aconfig │ │ ├── test_exported.aconfig │ │ ├── test_force_read_only.aconfig │ │ ├── test_second_package.aconfig │ │ └── third.values │ ├── aconfig_device_paths/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── mainline_aconfig_flags_paths.txt │ │ ├── partition_aconfig_flags_paths.txt │ │ ├── src/ │ │ │ ├── DeviceProtosTemplate.java │ │ │ ├── DeviceProtosTestUtilTemplate.java │ │ │ ├── HostDeviceProtosTemplate.java │ │ │ └── lib.rs │ │ └── test/ │ │ ├── Android.bp │ │ ├── AndroidManifest.xml │ │ └── src/ │ │ └── DeviceProtosTestUtilTest.java │ ├── aconfig_flags/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── flags.aconfig │ │ └── src/ │ │ └── lib.rs │ ├── aconfig_protos/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── jarjar-nano-rules.txt │ │ ├── protos/ │ │ │ ├── aconfig.proto │ │ │ └── aconfig_internal.proto │ │ └── src/ │ │ └── lib.rs │ ├── aconfig_storage_file/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── aconfig_storage_file.cpp │ │ ├── build.rs │ │ ├── include/ │ │ │ └── aconfig_storage/ │ │ │ └── aconfig_storage_file.hpp │ │ ├── protos/ │ │ │ └── aconfig_storage_metadata.proto │ │ ├── src/ │ │ │ ├── flag_info.rs │ │ │ ├── flag_table.rs │ │ │ ├── flag_value.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── package_table.rs │ │ │ ├── protos.rs │ │ │ ├── sip_hasher13.rs │ │ │ └── test_utils.rs │ │ ├── srcs/ │ │ │ └── android/ │ │ │ └── aconfig/ │ │ │ └── storage/ │ │ │ ├── AconfigStorageException.java │ │ │ ├── ByteBufferReader.java │ │ │ ├── FileType.java │ │ │ ├── FlagTable.java │ │ │ ├── FlagType.java │ │ │ ├── FlagValueList.java │ │ │ ├── PackageTable.java │ │ │ ├── SipHasher13.java │ │ │ ├── StorageFileProvider.java │ │ │ └── TableUtils.java │ │ └── tests/ │ │ ├── Android.bp │ │ ├── AndroidManifest.xml │ │ ├── AndroidStorageJaveTest.xml │ │ ├── data/ │ │ │ ├── v1/ │ │ │ │ ├── flag_v1.info │ │ │ │ └── flag_v1.val │ │ │ └── v2/ │ │ │ ├── flag_v2.info │ │ │ └── flag_v2.val │ │ ├── jarjar.txt │ │ ├── srcs/ │ │ │ ├── ByteBufferReaderTest.java │ │ │ ├── FlagTableTest.java │ │ │ ├── FlagValueListTest.java │ │ │ ├── PackageTableTest.java │ │ │ ├── SipHasher13Test.java │ │ │ ├── StorageFileProviderTest.java │ │ │ └── TestDataUtils.java │ │ └── storage_file_test.cpp │ ├── aconfig_storage_read_api/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── aconfig_storage_read_api.cpp │ │ ├── build.rs │ │ ├── include/ │ │ │ └── aconfig_storage/ │ │ │ └── aconfig_storage_read_api.hpp │ │ ├── src/ │ │ │ ├── flag_info_query.rs │ │ │ ├── flag_table_query.rs │ │ │ ├── flag_value_query.rs │ │ │ ├── lib.rs │ │ │ ├── mapped_file.rs │ │ │ └── package_table_query.rs │ │ ├── srcs/ │ │ │ ├── android/ │ │ │ │ ├── aconfig/ │ │ │ │ │ └── storage/ │ │ │ │ │ ├── AconfigStorageReadAPI.java │ │ │ │ │ ├── FlagReadContext.java │ │ │ │ │ └── PackageReadContext.java │ │ │ │ └── os/ │ │ │ │ └── flagging/ │ │ │ │ ├── PlatformAconfigPackage.java │ │ │ │ └── PlatformAconfigPackageInternal.java │ │ │ └── lib.rs │ │ └── tests/ │ │ ├── AconfigStorageReadFunctionalTest.xml │ │ ├── Android.bp │ │ ├── AndroidManifest.xml │ │ ├── data/ │ │ │ ├── v1/ │ │ │ │ ├── flag_v1.info │ │ │ │ └── flag_v1.val │ │ │ └── v2/ │ │ │ ├── flag_v2.info │ │ │ └── flag_v2.val │ │ ├── functional/ │ │ │ └── srcs/ │ │ │ ├── AconfigStorageReadAPITest.java │ │ │ ├── PlatformAconfigPackageInternalTest.java │ │ │ └── PlatformAconfigPackageTest.java │ │ ├── storage_read_api_test.cpp │ │ └── storage_read_api_test.rs │ ├── aconfig_storage_write_api/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── aconfig_storage_write_api.cpp │ │ ├── build.rs │ │ ├── include/ │ │ │ └── aconfig_storage/ │ │ │ └── aconfig_storage_write_api.hpp │ │ ├── src/ │ │ │ ├── flag_info_update.rs │ │ │ ├── flag_value_update.rs │ │ │ ├── lib.rs │ │ │ ├── mapped_file.rs │ │ │ └── test_utils.rs │ │ └── tests/ │ │ ├── Android.bp │ │ ├── flag.info │ │ ├── flag.val │ │ ├── storage_write_api_test.cpp │ │ └── storage_write_api_test.rs │ ├── aflags/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── aconfig_storage_source.rs │ │ ├── device_config_source.rs │ │ ├── load_protos.rs │ │ └── main.rs │ ├── convert_finalized_flags/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── extended_flags_list_35.txt │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ ├── exported_flag_check/ │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── allow_flag_list.txt │ │ ├── allow_package_list.txt │ │ ├── src/ │ │ │ ├── main.rs │ │ │ └── utils.rs │ │ └── tests/ │ │ ├── api-signature-file.txt │ │ ├── finalized-flags.txt │ │ ├── flags.declarations │ │ ├── flags.protobuf │ │ ├── flags.values │ │ └── generate-flags-protobuf.sh │ ├── fake_device_config/ │ │ ├── Android.bp │ │ └── src/ │ │ └── android/ │ │ ├── os/ │ │ │ ├── Build.java │ │ │ └── flagging/ │ │ │ ├── AconfigPackage.java │ │ │ ├── AconfigPackageInternal.java │ │ │ ├── PlatformAconfigPackage.java │ │ │ └── PlatformAconfigPackageInternal.java │ │ └── util/ │ │ └── Log.java │ └── overrideflags/ │ └── overrideflags.py ├── acp/ │ ├── Android.bp │ ├── README │ └── acp.c ├── apicheck/ │ ├── Android.bp │ └── etc/ │ └── apicheck ├── atree/ │ ├── Android.bp │ ├── atree.cpp │ ├── files.cpp │ ├── files.h │ ├── fs.cpp │ ├── fs.h │ └── options.h ├── auto_gen_test_config.py ├── auto_gen_test_config_test.py ├── brillo-clang-format ├── build-runfiles.cc ├── canoninja/ │ ├── README.md │ ├── canoninja.go │ ├── canoninja_test.go │ ├── cmd/ │ │ └── canoninja.go │ └── go.mod ├── characteristics_rro_generator.py ├── check-flagged-apis/ │ ├── Android.bp │ ├── OWNERS │ ├── check-flagged-apis.sh │ └── src/ │ └── com/ │ └── android/ │ └── checkflaggedapis/ │ ├── CheckFlaggedApisTest.kt │ └── Main.kt ├── check_elf_file.py ├── check_identical_lib.sh ├── check_radio_versions.py ├── compare_builds.py ├── compliance/ │ ├── Android.bp │ ├── README.md │ ├── cmd/ │ │ ├── checkmetadata/ │ │ │ ├── checkmetadata.go │ │ │ └── checkmetadata_test.go │ │ ├── checkshare/ │ │ │ ├── checkshare.go │ │ │ └── checkshare_test.go │ │ ├── dumpgraph/ │ │ │ ├── dumpgraph.go │ │ │ └── dumpgraph_test.go │ │ ├── dumpresolutions/ │ │ │ ├── dumpresolutions.go │ │ │ └── dumpresolutions_test.go │ │ ├── htmlnotice/ │ │ │ ├── htmlnotice.go │ │ │ └── htmlnotice_test.go │ │ ├── listshare/ │ │ │ ├── listshare.go │ │ │ └── listshare_test.go │ │ ├── rtrace/ │ │ │ ├── rtrace.go │ │ │ └── rtrace_test.go │ │ ├── shippedlibs/ │ │ │ ├── shippedlibs.go │ │ │ └── shippedlibs_test.go │ │ ├── testdata/ │ │ │ ├── README.md │ │ │ ├── firstparty/ │ │ │ │ ├── FIRST_PARTY_LICENSE │ │ │ │ ├── METADATA │ │ │ │ ├── application.meta_lic │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ ├── highest.apex.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── liba.so.meta_lic │ │ │ │ ├── libb.so.meta_lic │ │ │ │ ├── libc.a.meta_lic │ │ │ │ └── libd.so.meta_lic │ │ │ ├── notice/ │ │ │ │ ├── METADATA │ │ │ │ ├── NOTICE_LICENSE │ │ │ │ ├── application.meta_lic │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ ├── highest.apex.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── liba.so.meta_lic │ │ │ │ ├── libb.so.meta_lic │ │ │ │ ├── libc.a.meta_lic │ │ │ │ └── libd.so.meta_lic │ │ │ ├── proprietary/ │ │ │ │ ├── METADATA │ │ │ │ ├── PROPRIETARY_LICENSE │ │ │ │ ├── application.meta_lic │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ ├── highest.apex.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── liba.so.meta_lic │ │ │ │ ├── libb.so.meta_lic │ │ │ │ ├── libc.a.meta_lic │ │ │ │ └── libd.so.meta_lic │ │ │ ├── reciprocal/ │ │ │ │ ├── METADATA │ │ │ │ ├── RECIPROCAL_LICENSE │ │ │ │ ├── application.meta_lic │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ ├── highest.apex.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── liba.so.meta_lic │ │ │ │ ├── libb.so.meta_lic │ │ │ │ ├── libc.a.meta_lic │ │ │ │ └── libd.so.meta_lic │ │ │ ├── regressconcur/ │ │ │ │ ├── README.md │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ ├── bin3.meta_lic │ │ │ │ │ ├── bin4.meta_lic │ │ │ │ │ ├── bin5.meta_lic │ │ │ │ │ ├── bin6.meta_lic │ │ │ │ │ ├── bin7.meta_lic │ │ │ │ │ ├── bin8.meta_lic │ │ │ │ │ └── bin9.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── lib1.a.meta_lic │ │ │ │ ├── lib2.a.meta_lic │ │ │ │ ├── lib3.a.meta_lic │ │ │ │ ├── lib4.a.meta_lic │ │ │ │ ├── lib5.a.meta_lic │ │ │ │ ├── lib6.a.meta_lic │ │ │ │ ├── lib7.a.meta_lic │ │ │ │ ├── lib8.a.meta_lic │ │ │ │ └── lib9.a.meta_lic │ │ │ ├── regressgpl1/ │ │ │ │ ├── README.md │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── libapache.so.meta_lic │ │ │ │ ├── libc++.so.meta_lic │ │ │ │ └── libgpl.so.meta_lic │ │ │ ├── regressgpl2/ │ │ │ │ ├── README.md │ │ │ │ ├── bin/ │ │ │ │ │ ├── bin1.meta_lic │ │ │ │ │ ├── bin2.meta_lic │ │ │ │ │ └── bin3.meta_lic │ │ │ │ ├── container.zip.meta_lic │ │ │ │ └── lib/ │ │ │ │ ├── libapache.so.meta_lic │ │ │ │ ├── libc++.so.meta_lic │ │ │ │ └── libgpl.so.meta_lic │ │ │ └── restricted/ │ │ │ ├── METADATA │ │ │ ├── METADATA.android │ │ │ ├── RESTRICTED_LICENSE │ │ │ ├── application.meta_lic │ │ │ ├── bin/ │ │ │ │ ├── bin1.meta_lic │ │ │ │ ├── bin2.meta_lic │ │ │ │ └── bin3.meta_lic │ │ │ ├── container.zip.meta_lic │ │ │ ├── highest.apex.meta_lic │ │ │ └── lib/ │ │ │ ├── liba.so.meta_lic │ │ │ ├── libb.so.meta_lic │ │ │ ├── libc.a.meta_lic │ │ │ └── libd.so.meta_lic │ │ ├── textnotice/ │ │ │ ├── textnotice.go │ │ │ └── textnotice_test.go │ │ └── xmlnotice/ │ │ ├── xmlnotice.go │ │ └── xmlnotice_test.go │ ├── condition.go │ ├── condition_test.go │ ├── conditionset.go │ ├── conditionset_test.go │ ├── doc.go │ ├── go.mod │ ├── go.work │ ├── graph.go │ ├── noticeindex.go │ ├── policy_policy.go │ ├── policy_policy_test.go │ ├── policy_resolve.go │ ├── policy_resolve_test.go │ ├── policy_resolvenotices.go │ ├── policy_resolvenotices_test.go │ ├── policy_resolveprivacy.go │ ├── policy_resolveprivacy_test.go │ ├── policy_resolveshare.go │ ├── policy_resolveshare_test.go │ ├── policy_shareprivacyconflicts.go │ ├── policy_shareprivacyconflicts_test.go │ ├── policy_shipped.go │ ├── policy_shipped_test.go │ ├── policy_walk.go │ ├── policy_walk_test.go │ ├── projectmetadata/ │ │ ├── Android.bp │ │ ├── projectmetadata.go │ │ └── projectmetadata_test.go │ ├── readgraph.go │ ├── readgraph_test.go │ ├── resolution.go │ ├── resolutionset.go │ ├── resolutionset_test.go │ ├── test_util.go │ └── testfs/ │ ├── Android.bp │ └── testfs.go ├── dependency_mapper/ │ ├── Android.bp │ ├── OWNERS │ ├── README.md │ ├── proto/ │ │ └── dependency.proto │ ├── src/ │ │ └── com/ │ │ └── android/ │ │ └── dependencymapper/ │ │ ├── ClassDependenciesVisitor.java │ │ ├── ClassDependencyAnalyzer.java │ │ ├── ClassDependencyData.java │ │ ├── ClassRelevancyFilter.java │ │ ├── DependencyMapper.java │ │ ├── JavaSourceAnalyzer.java │ │ ├── JavaSourceData.java │ │ ├── Main.java │ │ └── Utils.java │ └── tests/ │ ├── res/ │ │ ├── testdata/ │ │ │ ├── annotation/ │ │ │ │ ├── AnnotationUsage.java │ │ │ │ ├── RuntimeAnnotation.java │ │ │ │ └── SourceAnnotation.java │ │ │ ├── constants/ │ │ │ │ ├── ConstantDefinition.java │ │ │ │ └── ConstantUsage.java │ │ │ ├── inheritance/ │ │ │ │ ├── BaseClass.java │ │ │ │ ├── BaseImpl.java │ │ │ │ └── InheritanceUsage.java │ │ │ └── methods/ │ │ │ ├── FieldUsage.java │ │ │ ├── MethodUsage.java │ │ │ ├── ReferenceClass1.java │ │ │ └── ReferenceClass2.java │ │ └── testfiles/ │ │ ├── dependency-mapper-test-data.jar │ │ └── sources.rsp │ └── src/ │ └── com/ │ └── android/ │ └── dependencymapper/ │ ├── ClassDependencyAnalyzerTest.java │ ├── ClassRelevancyFilterTest.java │ ├── DependencyMapperTest.java │ ├── JavaSourceAnalyzerTest.java │ └── UtilsTest.java ├── docker/ │ ├── .gitignore │ ├── Dockerfile │ └── README.md ├── droiddoc/ │ ├── Android.bp │ ├── LICENSE │ ├── README │ ├── templates-pdk/ │ │ ├── assets/ │ │ │ ├── android-developer-core.css │ │ │ ├── android-developer-docs-devguide.css │ │ │ ├── android-developer-docs.css │ │ │ ├── android-developer-docs.js │ │ │ ├── android-developer-reference.js │ │ │ ├── android-developer-resource-browser.css │ │ │ ├── android-developer-resource-browser.js │ │ │ ├── carousel.js │ │ │ ├── customizations.js │ │ │ ├── design/ │ │ │ │ ├── default.css │ │ │ │ └── default.js │ │ │ ├── jquery-history.js │ │ │ ├── microtemplate.js │ │ │ ├── prettify.js │ │ │ ├── search_autocomplete.js │ │ │ ├── style.css │ │ │ └── yui-3.3.0-reset-min.css │ │ ├── components/ │ │ │ └── masthead.cs │ │ ├── customizations.cs │ │ ├── data.hdf │ │ ├── docpage.cs │ │ ├── footer.cs │ │ ├── head_tag.cs │ │ ├── header_tabs.cs │ │ ├── jd_lists_unified.cs │ │ ├── sampleindex.cs │ │ ├── sdkpage.cs │ │ └── trailer.cs │ └── test/ │ ├── generics/ │ │ └── src/ │ │ └── com/ │ │ └── android/ │ │ └── generics/ │ │ ├── AbsListView.java │ │ ├── Adapter.java │ │ ├── AdapterView.java │ │ ├── Bar.java │ │ ├── Foo.java │ │ ├── FooBar.java │ │ ├── Iface.java │ │ ├── ListAdapter.java │ │ ├── TestComparable.java │ │ └── TestEnum.java │ └── stubs/ │ ├── expected/ │ │ └── com/ │ │ └── android/ │ │ └── stubs/ │ │ ├── Annot.java │ │ ├── InterfaceEnum.java │ │ ├── Parent.java │ │ ├── SomeEnum.java │ │ ├── Types.java │ │ ├── a/ │ │ │ ├── A.java │ │ │ └── SomeInterface.java │ │ └── b/ │ │ └── B.java │ ├── func.sh │ ├── run.sh │ └── src/ │ └── com/ │ └── android/ │ └── stubs/ │ ├── Annot.java │ ├── InterfaceEnum.java │ ├── Parent.java │ ├── SomeEnum.java │ ├── Types.java │ ├── a/ │ │ ├── A.java │ │ └── SomeInterface.java │ ├── b/ │ │ └── B.java │ └── hidden/ │ ├── Hidden.java │ ├── HiddenOuter.java │ └── PackagePrivate.java ├── edit_monitor/ │ ├── Android.bp │ ├── OWNERS │ ├── daemon_manager.py │ ├── daemon_manager_test.py │ ├── edit_monitor.py │ ├── edit_monitor_integration_test.py │ ├── edit_monitor_test.py │ ├── main.py │ ├── proto/ │ │ └── edit_event.proto │ ├── utils.py │ └── utils_test.py ├── envsetup/ │ ├── run_envsetup_tests │ └── spam_for_lunch ├── event_log_tags.py ├── exercise_compare_builds ├── extract_kernel.py ├── fat16copy.py ├── filelistdiff/ │ ├── Android.bp │ ├── OWNERS │ ├── allowlist │ ├── allowlist_next │ └── file_list_diff.py ├── fileslist_util.py ├── finalization/ │ ├── OWNERS │ ├── README.md │ ├── build-step-0-and-m.sh │ ├── build-step-0.sh │ ├── build-step-1-and-2.sh │ ├── build-step-1-and-m.sh │ ├── build-step-1.sh │ ├── build_soong_java_droidstubs.go.apply_hack.diff │ ├── build_soong_java_droidstubs.go.revert_hack.diff │ ├── cleanup.sh │ ├── command-line-options.sh │ ├── dryrun-cleanup.sh │ ├── dryrun-step-1-and-2.sh │ ├── dryrun-step-1.sh │ ├── environment.sh │ ├── finalize-sdk-rel.sh │ ├── finalize-sdk-resources.sh │ ├── finalize-vintf-resources.sh │ ├── frameworks_base.apply_hack.diff │ ├── frameworks_base.revert_hack.diff │ ├── localonly-steps.sh │ ├── step-0.sh │ ├── step-1.sh │ └── step-2.sh ├── find_static_candidates.py ├── findleaves.py ├── fixlinebreaks.sh ├── fs_config/ │ ├── Android.bp │ ├── OWNERS │ ├── README.md │ ├── end_to_end_test/ │ │ ├── config.fs │ │ ├── product_fs_config_dirs │ │ ├── product_fs_config_files │ │ ├── run_test.sh │ │ ├── system_fs_config_dirs │ │ ├── system_fs_config_files │ │ ├── vendor_fs_config_dirs │ │ └── vendor_fs_config_files │ ├── fs_config.c │ ├── fs_config.go │ ├── fs_config_generator.py │ ├── pylintrc │ └── test_fs_config_generator.py ├── generate-enforce-rro-android-manifest.py ├── generate-notice-files.py ├── generate-self-extracting-archive.py ├── generate_gts_shared_report.py ├── ide_query/ │ ├── OWNERS │ ├── cc_analyzer/ │ │ ├── Android.bp │ │ ├── README.md │ │ ├── analyzer.cc │ │ ├── analyzer.h │ │ ├── builtin_headers.cc │ │ ├── builtin_headers.h │ │ ├── include_scanner.cc │ │ ├── include_scanner.h │ │ └── main.cc │ ├── cc_analyzer_proto/ │ │ ├── Android.bp │ │ ├── cc_analyzer.pb.go │ │ ├── cc_analyzer.proto │ │ └── regen.sh │ ├── go.mod │ ├── go.work │ ├── go.work.sum │ ├── ide_query.go │ ├── ide_query.sh │ ├── ide_query_proto/ │ │ ├── ide_query.pb.go │ │ ├── ide_query.proto │ │ └── regen.sh │ └── prober_scripts/ │ ├── cpp/ │ │ ├── Android.bp │ │ ├── foo.proto │ │ └── general.cc │ ├── cpp_suite.textpb │ ├── ide_query.out │ ├── jvm/ │ │ ├── Android.bp │ │ ├── Bar.java │ │ ├── Foo.java │ │ ├── ide_query.out │ │ ├── other/ │ │ │ └── Other.java │ │ └── suite.textpb │ └── regen.sh ├── java-event-log-tags.py ├── libhost/ │ ├── Android.bp │ ├── CopyFile.c │ └── include/ │ └── host/ │ └── CopyFile.h ├── list_files.py ├── lunchable ├── merge-event-log-tags.py ├── missing_soong_module_info.py ├── mk2bp_catalog.py ├── mk2bp_partition.py ├── normalize_path.py ├── otatools_package/ │ └── Android.bp ├── perf/ │ ├── benchmarks │ ├── format_benchmarks │ ├── pretty.py │ └── utils.py ├── post_process_props.py ├── post_process_props_unittest.xml ├── print_module_licenses.sh ├── product_config/ │ ├── Android.bp │ ├── MANIFEST.MF │ ├── TEST_MANIFEST.MF │ ├── inherit_tree.py │ ├── src/ │ │ └── com/ │ │ └── android/ │ │ └── build/ │ │ └── config/ │ │ ├── CommandException.java │ │ ├── ConfigBase.java │ │ ├── ConvertMakeToGenericConfig.java │ │ ├── CsvParser.java │ │ ├── DumpConfigParser.java │ │ ├── ErrorReporter.java │ │ ├── Errors.java │ │ ├── FlatConfig.java │ │ ├── FlattenConfig.java │ │ ├── GenericConfig.java │ │ ├── Kati.java │ │ ├── KatiCommand.java │ │ ├── KatiCommandImpl.java │ │ ├── KatiImpl.java │ │ ├── Main.java │ │ ├── MakeConfig.java │ │ ├── MakeWriter.java │ │ ├── Options.java │ │ ├── OutputChecker.java │ │ ├── Position.java │ │ ├── RegexSet.java │ │ ├── Str.java │ │ ├── Value.java │ │ └── VarType.java │ ├── test/ │ │ └── com/ │ │ └── android/ │ │ └── build/ │ │ └── config/ │ │ ├── CsvParserTest.java │ │ ├── ErrorReporterTest.java │ │ ├── OptionsTest.java │ │ ├── PositionTest.java │ │ ├── TestErrors.java │ │ └── TestRunner.java │ └── test.sh ├── protos/ │ ├── Android.bp │ └── metadata_file.proto ├── rbcrun/ │ ├── Android.bp │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── host.go │ ├── host_test.go │ ├── rbcrun/ │ │ └── rbcrun.go │ └── testdata/ │ ├── bzl_loads_scl.bzl │ ├── bzl_loads_scl_2.bzl │ ├── file_ops.star │ ├── load.star │ ├── module1.star │ ├── module2.star │ ├── scl_incorrectly_loads_bzl.scl │ ├── shell.star │ └── test_scl.scl ├── record-finalized-flags/ │ ├── .gitignore │ ├── Android.bp │ ├── Cargo.toml │ ├── OWNERS │ ├── src/ │ │ ├── api_signature_files.rs │ │ ├── finalized_flags.rs │ │ ├── flag_values.rs │ │ └── main.rs │ └── tests/ │ ├── api-signature-file.txt │ ├── finalized-flags.txt │ ├── flags.declarations │ ├── flags.protobuf │ ├── flags.values │ └── generate-flags-protobuf.sh ├── releasetools/ │ ├── Android.bp │ ├── OWNERS │ ├── add_img_to_target_files.py │ ├── apex_utils.py │ ├── blockimgdiff.py │ ├── build_image.py │ ├── build_super_image.py │ ├── care_map_pb2.py │ ├── check_ota_package_signature.py │ ├── check_partition_sizes.py │ ├── check_target_files_signatures.py │ ├── check_target_files_vintf.py │ ├── common.py │ ├── create_brick_ota.py │ ├── edify_generator.py │ ├── find_shareduid_violation.py │ ├── fsverity_metadata_generator.py │ ├── images.py │ ├── img_from_target_files.py │ ├── jarjar-rules.txt │ ├── make_recovery_patch.py │ ├── merge/ │ │ ├── Android.bp │ │ ├── OWNERS │ │ ├── merge_builds.py │ │ ├── merge_compatibility_checks.py │ │ ├── merge_dexopt.py │ │ ├── merge_meta.py │ │ ├── merge_target_files.py │ │ ├── merge_utils.py │ │ ├── test_merge_compatibility_checks.py │ │ ├── test_merge_meta.py │ │ └── test_merge_utils.py │ ├── merge_ota.py │ ├── non_ab_ota.py │ ├── ota_from_raw_img.py │ ├── ota_from_target_files.py │ ├── ota_metadata.proto │ ├── ota_metadata_pb2.py │ ├── ota_package_parser.py │ ├── ota_signing_utils.py │ ├── ota_utils.py │ ├── payload_signer.py │ ├── pylintrc │ ├── rangelib.py │ ├── sign_apex.py │ ├── sign_target_files_apks.py │ ├── sparse_img.py │ ├── target_files_diff.py │ ├── test_add_img_to_target_files.py │ ├── test_apex_utils.py │ ├── test_blockimgdiff.py │ ├── test_build_image.py │ ├── test_check_partition_sizes.py │ ├── test_check_target_files_vintf.py │ ├── test_common.py │ ├── test_merge_ota.py │ ├── test_non_ab_ota.py │ ├── test_ota_from_target_files.py │ ├── test_ota_utils.py │ ├── test_rangelib.py │ ├── test_sign_apex.py │ ├── test_sign_target_files_apks.py │ ├── test_utils.py │ ├── test_validate_target_files.py │ ├── test_verity_utils.py │ ├── testdata/ │ │ ├── TestApp.apk │ │ ├── apexkeys_framework.txt │ │ ├── apexkeys_framework_conflict.txt │ │ ├── apexkeys_merge.txt │ │ ├── apexkeys_vendor.txt │ │ ├── apkcerts_framework.txt │ │ ├── apkcerts_merge.txt │ │ ├── apkcerts_vendor.txt │ │ ├── foo.apex │ │ ├── has_apk.apex │ │ ├── media.x509.pem │ │ ├── merge_config_framework_item_list │ │ ├── payload_signer.sh │ │ ├── platform.x509.pem │ │ ├── signing_helper.sh │ │ ├── testkey.key │ │ ├── testkey.pk8 │ │ ├── testkey.pubkey.pem │ │ ├── testkey.x509.pem │ │ ├── testkey_EC.key │ │ ├── testkey_RSA4096.key │ │ ├── testkey_mincrypt │ │ ├── testkey_with_passwd.key │ │ ├── testkey_with_passwd.pk8 │ │ ├── testkey_with_passwd.x509.pem │ │ ├── verity.x509.pem │ │ ├── verity_mincrypt │ │ └── vintf/ │ │ ├── kernel/ │ │ │ └── SYSTEM/ │ │ │ └── etc/ │ │ │ └── vintf/ │ │ │ └── compatibility_matrix.1.xml │ │ ├── matrix_incompat/ │ │ │ └── SYSTEM/ │ │ │ └── etc/ │ │ │ └── vintf/ │ │ │ └── compatibility_matrix.1.xml │ │ ├── sku_compat/ │ │ │ ├── ODM/ │ │ │ │ └── etc/ │ │ │ │ └── vintf/ │ │ │ │ └── manifest_sku.xml │ │ │ └── SYSTEM/ │ │ │ └── etc/ │ │ │ └── vintf/ │ │ │ └── compatibility_matrix.1.xml │ │ └── sku_incompat/ │ │ ├── ODM/ │ │ │ └── etc/ │ │ │ └── vintf/ │ │ │ └── manifest_sku.xml │ │ └── SYSTEM/ │ │ └── etc/ │ │ └── vintf/ │ │ └── compatibility_matrix.1.xml │ ├── validate_target_files.py │ └── verity_utils.py ├── sbom/ │ ├── Android.bp │ ├── compliance_metadata.py │ ├── gen_notice_xml.py │ ├── gen_sbom.py │ ├── generate-sbom-framework_res.py │ ├── generate-sbom.py │ ├── sbom_data.py │ ├── sbom_data_test.py │ ├── sbom_writers.py │ ├── sbom_writers_test.py │ └── testdata/ │ ├── expected_json_sbom.spdx.json │ ├── expected_tagvalue_sbom.spdx │ ├── expected_tagvalue_sbom_doc_describes_file.spdx │ └── expected_tagvalue_sbom_unbundled.spdx ├── signapk/ │ ├── Android.bp │ ├── OWNERS │ ├── SignApk.mf │ ├── src/ │ │ └── com/ │ │ └── android/ │ │ └── signapk/ │ │ ├── CountingOutputStream.java │ │ └── SignApk.java │ └── test/ │ └── run ├── signtos/ │ ├── Android.bp │ ├── SignTos.java │ └── SignTos.mf ├── soong_to_convert.py ├── stub_diff_analyzer.py ├── test_extract_kernel.py ├── test_post_process_props.py ├── tool_event_logger/ │ ├── Android.bp │ ├── OWNERS │ ├── proto/ │ │ └── tool_event.proto │ ├── tool_event_logger.py │ └── tool_event_logger_test.py ├── warn/ │ ├── .pylintrc │ ├── OWNERS │ ├── __init__.py │ ├── android_project_list.py │ ├── chrome_project_list.py │ ├── cpp_warn_patterns.py │ ├── html_writer.py │ ├── java_warn_patterns.py │ ├── make_warn_patterns.py │ ├── other_warn_patterns.py │ ├── severity.py │ ├── tidy_warn_patterns.py │ ├── warn.py │ └── warn_common.py ├── warn.py ├── whichgit ├── zipalign/ │ ├── Android.bp │ ├── OWNERS │ ├── README.txt │ ├── ZipAlign.cpp │ ├── ZipAlignMain.cpp │ ├── ZipEntry.cpp │ ├── ZipEntry.h │ ├── ZipFile.cpp │ ├── ZipFile.h │ ├── include/ │ │ └── ZipAlign.h │ └── tests/ │ └── src/ │ └── align_test.cpp └── ziptime/ ├── Android.bp ├── README.txt ├── ZipEntry.cpp ├── ZipEntry.h ├── ZipFile.cpp ├── ZipFile.h └── ZipTime.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml *.pyc *.swp blueprint/ kati/ soong/ ================================================ FILE: Android.bp ================================================ // Copyright 2024 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], } // Package the minimal files required to run envsetup.sh in the test // environment. genrule { name: "envsetup_minimum.zip", visibility: [ "//build/make/tests:__subpackages__", ], tools: [ "soong_zip", ], srcs: [ "envsetup.sh", "shell_utils.sh", "core/envsetup.mk", ], out: ["envsetup.zip"], cmd: "$(location soong_zip) -o $(out) -D build/make", } ================================================ FILE: Changes.md ================================================ # Build System Changes for Android.mk/Android.bp Writers ## Soong genrules are now sandboxed Previously, soong genrules could access any files in the source tree, without specifying them as inputs. This makes them incorrect in incremental builds, and incompatible with RBE and Bazel. Now, genrules are sandboxed so they can only access their listed srcs. Modules denylisted in genrule/allowlists.go are exempt from this. You can also set `BUILD_BROKEN_GENRULE_SANDBOXING` in board config to disable this behavior. ## Partitions are no longer affected by previous builds Partition builds used to include everything in their staging directories, and building an individual module will install it to the staging directory. Thus, previously, `m mymodule` followed by `m` would cause `mymodule` to be presinstalled on the device, even if it wasn't listed in `PRODUCT_PACKAGES`. This behavior has been changed, and now the partition images only include what they'd have if you did a clean build. This behavior can be disabled by setting the `BUILD_BROKEN_INCORRECT_PARTITION_IMAGES` environment variable or board config variable. Manually adding make rules that build to the staging directories without going through the make module system will not be compatible with this change. This includes many usages of `LOCAL_POST_INSTALL_CMD`. ## Perform validation of Soong plugins Each Soong plugin will require manual work to migrate to Bazel. In order to minimize the manual work outside of build/soong, we are restricting plugins to those that exist today and those in vendor or hardware directories. If you need to extend the build system via a plugin, please reach out to the build team via email android-building@googlegroups.com (external) for any questions, or see [go/soong](http://go/soong) (internal). To omit the validation, `BUILD_BROKEN_PLUGIN_VALIDATION` expects a space-separated list of plugins to omit from the validation. This must be set within a product configuration .mk file, board config .mk file, or buildspec.mk. ## Python 2 to 3 migration Python 2 has been completely removed from the build. Please migrate any remaining usages to Python 3, and remove any version-specific properties from bp files. ## Stop referencing sysprop_library directly from cc modules For the migration to Bazel, we are no longer mapping sysprop_library targets to their generated `cc_library` counterparts when dependning on them from a cc module. Instead, directly depend on the generated module by prefixing the module name with `lib`. For example, depending on the following module: ``` sysprop_library { name: "foo", srcs: ["foo.sysprop"], } ``` from a module named `bar` can be done like so: ``` cc_library { name: "bar", srcs: ["bar.cc"], deps: ["libfoo"], } ``` Failure to do this will result in an error about a missing variant. ## Gensrcs starts disallowing depfile property To migrate all gensrcs to Bazel, we are restricting the use of depfile property because Bazel requires specifying the dependencies directly. To fix existing uses, remove depfile and directly specify all the dependencies in .bp files. For example: ``` gensrcs { name: "framework-cppstream-protos", tools: [ "aprotoc", "protoc-gen-cppstream", ], cmd: "mkdir -p $(genDir)/$(in) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-cppstream) " + " -I . " + " $(in) ", srcs: [ "bar.proto", ], output_extension: "srcjar", } ``` where `bar.proto` imports `external.proto` would become ``` gensrcs { name: "framework-cppstream-protos", tools: [ "aprotoc", "protoc-gen-cpptream", ], tool_files: [ "external.proto", ], cmd: "mkdir -p $(genDir)/$(in) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-cppstream) " + " $(in) ", srcs: [ "bar.proto", ], output_extension: "srcjar", } ``` as in https://android-review.googlesource.com/c/platform/frameworks/base/+/2125692/. `BUILD_BROKEN_DEPFILE` can be used to allowlist usage of depfile in `gensrcs`. If `depfile` is needed for generating javastream proto, `java_library` with `proto.type` set `stream` is the alternative solution. Sees https://android-review.googlesource.com/c/platform/packages/modules/Permission/+/2118004/ for an example. ## Genrule starts disallowing directory inputs To better specify the inputs to the build, we are restricting use of directories as inputs to genrules. To fix existing uses, change inputs to specify the inputs and update the command accordingly. For example: ``` genrule: { name: "foo", srcs: ["bar"], cmd: "cp $(location bar)/*.xml $(gendir)", ... } ``` would become ``` genrule: { name: "foo", srcs: ["bar/*.xml"], cmd: "cp $(in) $(gendir)", ... } ``` `BUILD_BROKEN_INPUT_DIR_MODULES` can be used to allowlist specific directories with genrules that have input directories. ## Dexpreopt starts enforcing `` checks (for Java modules) In order to construct correct class loader context for dexpreopt, build system needs to know about the shared library dependencies of Java modules listed in the `` tags in the manifest. Since the build system does not have access to the manifest contents, that information must be present in the build files. In simple cases Soong is able to infer it from its knowledge of Java SDK libraries and the `libs` property in Android.bp, but in more complex cases it is necessary to add the missing information in Android.bp/Android.mk manually. To specify a list of libraries for a given modules, use: * Android.bp properties: `uses_libs`, `optional_uses_libs` * Android.mk variables: `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_USES_LIBRARIES` If a library is in `libs`, it usually should *not* be added to the above properties, and Soong should be able to infer the `` tag. But sometimes a library also needs additional information in its Android.bp/Android.mk file (e.g. when it is a `java_library` rather than a `java_sdk_library`, or when the library name is different from its module name, or when the module is defined in Android.mk rather than Android.bp). In such cases it is possible to tell the build system that the library provides a `` with a given name (however, this is discouraged and will be deprecated in the future, and it is recommended to fix the underlying problem): * Android.bp property: `provides_uses_lib` * Android.mk variable: `LOCAL_PROVIDES_USES_LIBRARY` It is possible to disable the check on a per-module basis. When doing that it is also recommended to disable dexpreopt, as disabling a failed check will result in incorrect class loader context recorded in the .odex file, which will cause class loader context mismatch and dexopt at first boot. * Android.bp property: `enforce_uses_lib` * Android.mk variable: `LOCAL_ENFORCE_USES_LIBRARIES` Finally, it is possible to globally disable the check: * For a given product: `PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true` * On the command line: `RELAX_USES_LIBRARY_CHECK=true` The environment variable overrides the product variable, so it is possible to disable the check for a product, but quickly re-enable it for a local build. ## `LOCAL_REQUIRED_MODULES` requires listed modules to exist {#BUILD_BROKEN_MISSING_REQUIRED_MODULES} Modules listed in `LOCAL_REQUIRED_MODULES`, `LOCAL_HOST_REQUIRED_MODULES` and `LOCAL_TARGET_REQUIRED_MODULES` need to exist unless `ALLOW_MISSING_DEPENDENCIES` is set. To temporarily relax missing required modules check, use: `BUILD_BROKEN_MISSING_REQUIRED_MODULES := true` ## Changes in system properties settings ### Product variables System properties for each of the partition is supposed to be set via following product config variables. For system partition, * `PRODUCT_SYSTEM_PROPERTIES` * `PRODUCT_SYSTEM_DEFAULT_PROPERTIES` is highly discouraged. Will be deprecated. For vendor partition, * `PRODUCT_VENDOR_PROPERTIES` * `PRODUCT_PROPERTY_OVERRIDES` is highly discouraged. Will be deprecated. * `PRODUCT_DEFAULT_PROPERTY_OVERRIDES` is also discouraged. Will be deprecated. For odm partition, * `PRODUCT_ODM_PROPERTIES` For system_ext partition, * `PRODUCT_SYSTEM_EXT_PROPERTIES` For product partition, * `PRODUCT_PRODUCT_PROPERTIES` ### Duplication is not allowed within a partition For each partition, having multiple sysprop assignments for the same name is prohibited. For example, the following will now trigger an error: `PRODUCT_VENDOR_PROPERTIES += foo=true foo=false` Having duplication across partitions are still allowed. So, the following is not an error: `PRODUCT_VENDOR_PROPERTIES += foo=true` `PRODUCT_SYSTEM_PROPERTIES += foo=false` In that case, the final value is determined at runtime. The precedence is * product * odm * vendor * system_ext * system So, `foo` becomes `true` because vendor has higher priority than system. To temporarily turn the build-time restriction off, use `BUILD_BROKEN_DUP_SYSPROP := true` ### Optional assignments System properties can now be set as optional using the new syntax: `name ?= value` Then the system property named `name` gets the value `value` only when there is no other non-optional assignments having the same name. For example, the following is allowed and `foo` gets `true` `PRODUCT_VENDOR_PROPERTIES += foo=true foo?=false` Note that the order between the optional and the non-optional assignments doesn't matter. The following gives the same result as above. `PRODUCT_VENDOR_PROPERTIES += foo?=false foo=true` Optional assignments can be duplicated and in that case their order matters. Specifically, the last one eclipses others. `PRODUCT_VENDOR_PROPERTIES += foo?=apple foo?=banana foo?=mango` With above, `foo` becomes `mango` since its the last one. Note that this behavior is different from the previous behavior of preferring the first one. To go back to the original behavior for compatability reason, use: `BUILD_BROKEN_DUP_SYSPROP := true` ## ELF prebuilts in `PRODUCT_COPY_FILES` {#BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES} ELF prebuilts in `PRODUCT_COPY_FILES` that are installed into these paths are an error: * `/bin/*` * `/lib/*` * `/lib64/*` Define prebuilt modules and add them to `PRODUCT_PACKAGES` instead. To temporarily relax this check and restore the behavior prior to this change, set `BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES := true` in `BoardConfig.mk`. ## COPY_HEADERS usage now produces warnings {#copy_headers} We've considered `BUILD_COPY_HEADERS`/`LOCAL_COPY_HEADERS` to be deprecated for a long time, and the places where it's been able to be used have shrinked over the last several releases. Equivalent functionality is not available in Soong. See the [build/soong/docs/best_practices.md#headers] for more information about how best to handle headers in Android. ## `m4` is not available on `$PATH` There is a prebuilt of it available in prebuilts/build-tools, and a make variable `M4` that contains the path. Beyond the direct usage, whenever you use bison or flex directly, they call m4 behind the scene, so you must set the M4 environment variable (and depend upon it for incremental build correctness): ``` $(intermediates)/foo.c: .KATI_IMPLICIT_OUTPUTS := $(intermediates)/foo.h $(intermediates)/foo.c: $(LOCAL_PATH)/foo.y $(M4) $(BISON) $(BISON_DATA) M4=$(M4) $(BISON) ... ``` ## Rules executed within limited environment With `ALLOW_NINJA_ENV=false` (soon to be the default), ninja, and all the rules/actions executed within it will only have access to a limited number of environment variables. Ninja does not track when environment variables change in order to trigger rebuilds, so changing behavior based on arbitrary variables is not safe with incremental builds. Kati and Soong can safely use environment variables, so the expectation is that you'd embed any environment variables that you need to use within the command line generated by those tools. See the [export section](#export_keyword) below for examples. For a temporary workaround, you can set `ALLOW_NINJA_ENV=true` in your environment to restore the previous behavior, or set `BUILD_BROKEN_NINJA_USES_ENV_VAR := ...` in your `BoardConfig.mk` to allow specific variables to be passed through until you've fixed the rules. ## LOCAL_C_INCLUDES outside the source/output trees are an error {#BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS} Include directories are expected to be within the source tree (or in the output directory, generated during the build). This has been checked in some form since Oreo, but now has better checks. There's now a `BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS` variable, that when set, will turn these errors into warnings temporarily. I don't expect this to last more than a release, since they're fairly easy to clean up. Neither of these cases are supported by Soong, and will produce errors when converting your module. ### Absolute paths This has been checked since Oreo. The common reason to hit this is because a makefile is calculating a path, and ran abspath/realpath/etc. This is a problem because it makes your build non-reproducible. It's very unlikely that your source path is the same on every machine. ### Using `../` to leave the source/output directories This is the new check that has been added. In every case I've found, this has been a mistake in the Android.mk -- assuming that `LOCAL_C_INCLUDES` (which is relative to the top of the source tree) acts like `LOCAL_SRC_FILES` (which is relative to `LOCAL_PATH`). Since this usually isn't a valid path, you can almost always just remove the offending line. ## `BOARD_HAL_STATIC_LIBRARIES` and `LOCAL_HAL_STATIC_LIBRARIES` are obsolete {#BOARD_HAL_STATIC_LIBRARIES} Define proper HIDL / Stable AIDL HAL instead. * For libhealthd, use health HAL. See instructions for implementing health HAL: * [hardware/interfaces/health/2.1/README.md] for health 2.1 HAL (recommended) * [hardware/interfaces/health/1.0/README.md] for health 1.0 HAL * For libdumpstate, use at least Dumpstate HAL 1.0. ## PRODUCT_STATIC_BOOT_CONTROL_HAL is obsolete {#PRODUCT_STATIC_BOOT_CONTROL_HAL} `PRODUCT_STATIC_BOOT_CONTROL_HAL` was the workaround to allow sideloading with statically linked boot control HAL, before shared library HALs were supported under recovery. Android Q has added such support (HALs will be loaded in passthrough mode), and the workarounds are being removed. Targets should build and install the recovery variant of boot control HAL modules into recovery image, similar to the ones installed for normal boot. See the change to crosshatch for example of this: * [device/google/crosshatch/bootctrl/Android.bp] for `bootctrl.sdm845` building rules * [device/google/crosshatch/device.mk] for installing `bootctrl.sdm845.recovery` and `android.hardware.boot@1.0-impl.recovery` into recovery image [device/google/crosshatch/bootctrl/Android.bp]: https://android.googlesource.com/device/google/crosshatch/+/master/bootctrl/Android.bp [device/google/crosshatch/device.mk]: https://android.googlesource.com/device/google/crosshatch/+/master/device.mk ## Deprecation of `BUILD_*` module types See [build/make/Deprecation.md](Deprecation.md) for the current status. ## `PRODUCT_HOST_PACKAGES` split from `PRODUCT_PACKAGES` {#PRODUCT_HOST_PACKAGES} Previously, adding a module to `PRODUCT_PACKAGES` that supported both the host and the target (`host_supported` in Android.bp; two modules with the same name in Android.mk) would cause both to be built and installed. In many cases you only want either the host or target versions to be built/installed by default, and would be over-building with both. So `PRODUCT_PACKAGES` will be changing to just affect target modules, while `PRODUCT_HOST_PACKAGES` is being added for host modules. Functional differences between `PRODUCT_PACKAGES` and `PRODUCT_HOST_PACKAGES`: * `PRODUCT_HOST_PACKAGES` does not have `_ENG`/`_DEBUG` variants, as that's a property of the target, not the host. * `PRODUCT_HOST_PACKAGES` does not support `LOCAL_MODULE_OVERRIDES`. * `PRODUCT_HOST_PACKAGES` requires listed modules to exist, and be host modules. (Unless `ALLOW_MISSING_DEPENDENCIES` is set) This is still an active migration, so currently it still uses `PRODUCT_PACKAGES` to make installation decisions, but verifies that if we used `PRODUCT_HOST_PACKAGES`, it would trigger installation for all of the same host packages. This check ignores shared libraries, as those are not normally necessary in `PRODUCT_*PACKAGES`, and tended to be over-built (especially the 32-bit variants). Future changes will switch installation decisions to `PRODUCT_HOST_PACKAGES` for host modules, error when there's a host-only module in `PRODUCT_PACKAGES`, and do some further cleanup where `LOCAL_REQUIRED_MODULES` are still merged between host and target modules with the same name. ## `*.c.arm` / `*.cpp.arm` deprecation {#file_arm} In Android.mk files, you used to be able to change LOCAL_ARM_MODE for each source file by appending `.arm` to the end of the filename in `LOCAL_SRC_FILES`. Soong does not support this uncommonly used behavior, instead expecting those files to be split out into a separate static library that chooses `arm` over `thumb` for the entire library. This must now also be done in Android.mk files. ## Windows cross-compiles no longer supported in Android.mk Modules that build for Windows (our only `HOST_CROSS` OS currently) must now be defined in `Android.bp` files. ## `LOCAL_MODULE_TAGS := eng debug` are obsolete {#LOCAL_MODULE_TAGS} `LOCAL_MODULE_TAGS` value `eng` and `debug` are now obsolete. They allowed modules to specify that they should always be installed on `-eng`, or `-eng` and `-userdebug` builds. This conflicted with the ability for products to specify which modules should be installed, effectively making it impossible to build a stripped down product configuration that did not include those modules. For the equivalent functionality, specify the modules in `PRODUCT_PACKAGES_ENG` or `PRODUCT_PACKAGES_DEBUG` in the appropriate product makefiles. Core android packages like `su` got added to the list in `build/make/target/product/base_system.mk`, but for device-specific modules there are often better base product makefiles to use instead. ## `USER` deprecation {#USER} `USER` will soon be `nobody` in many cases due to the addition of a sandbox around the Android build. Most of the time you shouldn't need to know the identity of the user running the build, but if you do, it's available in the make variable `BUILD_USERNAME` for now. Similarly, the `hostname` tool will also be returning a more consistent value of `android-build`. The real value is available as `BUILD_HOSTNAME`. ## `BUILD_NUMBER` removal from Android.mk {#BUILD_NUMBER} `BUILD_NUMBER` should not be used directly in Android.mk files, as it would trigger them to be re-read every time the `BUILD_NUMBER` changes (which it does on every build server build). If possible, just remove the use so that your builds are more reproducible. If you do need it, use `BUILD_NUMBER_FROM_FILE`: ``` make $(LOCAL_BUILT_MODULE): mytool --build_number $(BUILD_NUMBER_FROM_FILE) -o $@ ``` That will expand out to a subshell that will read the current `BUILD_NUMBER` whenever it's run. It will not re-run your command if the build number has changed, so incremental builds will have the build number from the last time the particular output was rebuilt. ## `DIST_DIR`, `dist_goal`, and `dist-for-goals` {#dist} `DIST_DIR` and `dist_goal` are no longer available when reading Android.mk files (or other build tasks). Always use `dist-for-goals` instead, which takes a PHONY goal, and a list of files to copy to `$DIST_DIR`. Whenever `dist` is specified, and the goal would be built (either explicitly on the command line, or as a dependency of something on the command line), that file will be copied into `$DIST_DIR`. For example, ``` make $(call dist-for-goals,foo,bar/baz) ``` will copy `bar/baz` into `$DIST_DIR/baz` when `m foo dist` is run. #### FILE_NAME_TAG {#FILE_NAME_TAG} To embed the `BUILD_NUMBER` (or for local builds, `eng.${USER}`), include `FILE_NAME_TAG_PLACEHOLDER` in the destination: ``` make # you can use dist-for-goals-with-filenametag function $(call dist-for-goals-with-filenametag,foo,bar.zip) # or use FILE_NAME_TAG_PLACEHOLDER manually $(call dist-for-goals,foo,bar.zip:baz-FILE_NAME_TAG_PLACEHOLDER.zip) ``` Which will produce `$DIST_DIR/baz-1234567.zip` on build servers which set `BUILD_NUMBER=1234567`, or `$DIST_DIR/baz-eng.builder.zip` for local builds. If you just want to append `BUILD_NUMBER` at the end of basename, use `dist-for-goals-with-filenametag` instead of `dist-for-goals`. #### Renames during copy Instead of specifying just a file, a destination name can be specified, including subdirectories: ``` make $(call dist-for-goals,foo,bar/baz:logs/foo.log) ``` will copy `bar/baz` into `$DIST_DIR/logs/foo.log` when `m foo dist` is run. ## `.PHONY` rule enforcement {#phony_targets} There are several new warnings/errors meant to ensure the proper use of `.PHONY` targets in order to improve the speed and reliability of incremental builds. `.PHONY`-marked targets are often used as shortcuts to provide "friendly" names for real files to be built, but any target marked with `.PHONY` is also always considered dirty, needing to be rebuilt every build. This isn't a problem for aliases or one-off user-requested operations, but if real builds steps depend on a `.PHONY` target, it can get quite expensive for what should be a tiny build. ``` make ...mk:42: warning: PHONY target "out/.../foo" looks like a real file (contains a "/") ``` Between this warning and the next, we're requiring that `.PHONY` targets do not have "/" in them, and real file targets do have a "/". This makes it more obvious when reading makefiles what is happening, and will help the build system differentiate these in the future too. ``` make ...mk:42: warning: writing to readonly directory: "kernel-modules" ``` This warning will show up for one of two reasons: 1. The target isn't intended to be a real file, and should be marked with `.PHONY`. This would be the case for this example. 2. The target is a real file, but it's outside the output directories. All outputs from the build system should be within the output directory, otherwise `m clean` is unable to clean the build, and future builds may not work properly. ``` make ...mk:42: warning: real file "out/.../foo" depends on PHONY target "buildbins" ``` If the first target isn't intended to be a real file, then it should be marked with `.PHONY`, which will satisfy this warning. This isn't the case for this example, as we require `.PHONY` targets not to have '/' in them. If the second (PHONY) target is a real file, it may unnecessarily be marked with `.PHONY`. ### `.PHONY` and calling other build systems One common pattern (mostly outside AOSP) that we've seen hit these warning is when building with external build systems (firmware, bootloader, kernel, etc). Those are often marked as `.PHONY` because the Android build system doesn't have enough dependencies to know when to run the other build system again during an incremental build. We recommend to build these outside of Android, and deliver prebuilts into the Android tree instead of decreasing the speed and reliability of the incremental Android build. In cases where that's not desired, to preserve the speed of Android incrementals, over-specifying dependencies is likely a better option than marking it with `.PHONY`: ``` make out/target/.../zImage: $(sort $(shell find -L $(KERNEL_SRCDIR))) ... ``` For reliability, many of these other build systems do not guarantee the same level of incremental build assurances as the Android Build is attempting to do -- without custom checks, Make doesn't rebuild objects when CFLAGS change, etc. In order to fix this, our recommendation is to do clean builds for each of these external build systems every time anything they rely on changes. For relatively smaller builds (like the kernel), this may be reasonable as long as you're not trying to actively debug the kernel. ## `export` and `unexport` deprecation {#export_keyword} The `export` and `unexport` keywords are obsolete, and will throw errors when used. Device specific configuration should not be able to affect common core build steps -- we're looking at triggering build steps to be invalidated if the set of environment variables they can access changes. If device specific configuration is allowed to change those, switching devices with the same output directory could become significantly more expensive than it already can be. If used during Android.mk files, and later tasks, it is increasingly likely that they are being used incorrectly. Attempting to change the environment for a single build step, and instead setting it for hundreds of thousands. It is not recommended to just move the environment variable setting outside of the build (in vendorsetup.sh, or some other configuration script or wrapper). We expect to limit the environment variables that the build respects in the future, others will be cleared. (There will be methods to get custom variables into the build, just not to every build step) Instead, write the export commands into the rule command lines themselves: ``` make $(intermediates)/generated_output.img: rm -rf $@ export MY_ENV_A="$(MY_A)"; make ... ``` If you want to set many environment variables, and/or use them many times, write them out to a script and source the script: ``` make envsh := $(intermediates)/env.sh $(envsh): rm -rf $@ echo 'export MY_ENV_A="$(MY_A)"' >$@ echo 'export MY_ENV_B="$(MY_B)"' >>$@ $(intermediates)/generated_output.img: PRIVATE_ENV := $(envsh) $(intermediates)/generated_output.img: $(envsh) a/b/c/package.sh rm -rf $@ source $(PRIVATE_ENV); make ... source $(PRIVATE_ENV); a/b/c/package.sh ... ``` ## Implicit make rules are obsolete {#implicit_rules} Implicit rules look something like the following: ``` make $(TARGET_OUT_SHARED_LIBRARIES)/%_vendor.so: $(TARGET_OUT_SHARED_LIBRARIES)/%.so ... %.o : %.foo ... ``` These can have wide ranging effects across unrelated modules, so they're now obsolete. Instead, use static pattern rules, which are similar, but explicitly match the specified outputs: ``` make libs := $(foreach lib,libfoo libbar,$(TARGET_OUT_SHARED_LIBRARIES)/$(lib)_vendor.so) $(libs): %_vendor.so: %.so ... files := $(wildcard $(LOCAL_PATH)/*.foo) gen := $(patsubst $(LOCAL_PATH)/%.foo,$(intermediates)/%.o,$(files)) $(gen): %.o : %.foo ... ``` ## Removing '/' from Valid Module Names {#name_slash} The build system uses module names in path names in many places. Having an extra '/' or '../' being inserted can cause problems -- and not just build breaks, but stranger invalid behavior. In every case we've seen, the fix is relatively simple: move the directory into `LOCAL_MODULE_RELATIVE_PATH` (or `LOCAL_MODULE_PATH` if you're still using it). If this causes multiple modules to be named the same, use unique module names and `LOCAL_MODULE_STEM` to change the installed file name: ``` make include $(CLEAR_VARS) LOCAL_MODULE := ver1/code.bin LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware ... include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := ver2/code.bin LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/firmware ... include $(BUILD_PREBUILT) ``` Can be rewritten as: ``` include $(CLEAR_VARS) LOCAL_MODULE := ver1_code.bin LOCAL_MODULE_STEM := code.bin LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver1 ... include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := ver2_code.bin LOCAL_MODULE_STEM := code.bin LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware/ver2 ... include $(BUILD_PREBUILT) ``` You just need to make sure that any other references (`PRODUCT_PACKAGES`, `LOCAL_REQUIRED_MODULES`, etc) are converted to the new names. ## Valid Module Names {#name} We've adopted lexical requirements very similar to [Bazel's requirements](https://docs.bazel.build/versions/master/build-ref.html#name) for target names. Valid characters are `a-z`, `A-Z`, `0-9`, and the special characters `_.+-=,@~`. This currently applies to `LOCAL_PACKAGE_NAME`, `LOCAL_MODULE`, and `LOCAL_MODULE_SUFFIX`, and `LOCAL_MODULE_STEM*`. Many other characters already caused problems if you used them, so we don't expect this to have a large effect. ## PATH Tools {#PATH_Tools} The build has started restricting the external host tools usable inside the build. This will help ensure that build results are reproducible across different machines, and catch mistakes before they become larger issues. To start with, this includes replacing the $PATH with our own directory of tools, mirroring that of the host PATH. The only difference so far is the removal of the host GCC tools. Anything that is not explicitly in the configuration as allowed will continue functioning, but will generate a log message. This is expected to become more restrictive over time. The configuration is located in build/soong/ui/build/paths/config.go, and contains all the common tools in use in many builds. Anything not in that list will currently print a warning in the `$OUT_DIR/soong.log` file, including the command and arguments used, and the process tree in order to help locate the usage. In order to fix any issues brought up by these checks, the best way to fix them is to use tools checked into the tree -- either as prebuilts, or building them as host tools during the build. As a temporary measure, you can set `TEMPORARY_DISABLE_PATH_RESTRICTIONS=true` in your environment to temporarily turn off the error checks and allow any tool to be used (with logging). Beware that GCC didn't work well with the interposer used for logging, so this may not help in all cases. ## Deprecating / obsoleting envsetup.sh variables in Makefiles It is not required to source envsetup.sh before running a build. Many scripts, including a majority of our automated build systems, do not do so. Make will transparently make every environment variable available as a make variable. This means that relying on environment variables only set up in envsetup.sh will produce different output for local users and scripted users. Many of these variables also include absolute path names, which we'd like to keep out of the generated files, so that you don't need to do a full rebuild if you move the source tree. To fix this, we're marking the variables that are set in envsetup.sh as deprecated in the makefiles. This will trigger a warning every time one is read (or written) inside Kati. Once all the warnings have been removed for a particular variable, we'll switch it to obsolete, and any references will become errors. ### envsetup.sh variables with make equivalents | instead of | use | |--------------------------------------------------------------|----------------------| | OUT {#OUT} | PRODUCT_OUT | | ANDROID_HOST_OUT {#ANDROID_HOST_OUT} | HOST_OUT | | ANDROID_PRODUCT_OUT {#ANDROID_PRODUCT_OUT} | PRODUCT_OUT | | ANDROID_HOST_OUT_TESTCASES {#ANDROID_HOST_OUT_TESTCASES} | HOST_OUT_TESTCASES | | ANDROID_TARGET_OUT_TESTCASES {#ANDROID_TARGET_OUT_TESTCASES} | TARGET_OUT_TESTCASES | All of the make variables may be relative paths from the current directory, or absolute paths if the output directory was specified as an absolute path. If you need an absolute variable, convert it to absolute during a rule, so that it's not expanded into the generated ninja file: ``` make $(PRODUCT_OUT)/gen.img: my/src/path/gen.sh export PRODUCT_OUT=$$(cd $(PRODUCT_OUT); pwd); cd my/src/path; ./gen.sh -o $${PRODUCT_OUT}/gen.img ``` ### ANDROID_BUILD_TOP {#ANDROID_BUILD_TOP} In Android.mk files, you can always assume that the current directory is the root of the source tree, so this can just be replaced with '.' (which is what $TOP is hardcoded to), or removed entirely. If you need an absolute path, see the instructions above. ### Stop using PATH directly {#PATH} This isn't only set by envsetup.sh, but it is modified by it. Due to that it's rather easy for this to change between different shells, and it's not ideal to reread the makefiles every time this changes. In most cases, you shouldn't need to touch PATH at all. When you need to have a rule reference a particular binary that's part of the source tree or outputs, it's preferrable to just use the path to the file itself (since you should already be adding that as a dependency). Depending on the rule, passing the file path itself may not be feasible due to layers of unchangable scripts/binaries. In that case, be sure to add the dependency, but modify the PATH within the rule itself: ``` make $(TARGET): myscript my/path/binary PATH=my/path:$$PATH myscript -o $@ ``` ### Stop using PYTHONPATH directly {#PYTHONPATH} Like PATH, this isn't only set by envsetup.sh, but it is modified by it. Due to that it's rather easy for this to change between different shells, and it's not ideal to reread the makefiles every time. The best solution here is to start switching to Soong's python building support, which packages the python interpreter, libraries, and script all into one file that no longer needs PYTHONPATH. See fontchain_lint for examples of this: * [external/fonttools/Lib/fontTools/Android.bp] for python_library_host * [frameworks/base/Android.bp] for python_binary_host * [frameworks/base/data/fonts/Android.mk] to execute the python binary If you still need to use PYTHONPATH, do so within the rule itself, just like path: ``` make $(TARGET): myscript.py $(sort $(shell find my/python/lib -name '*.py')) PYTHONPATH=my/python/lib:$$PYTHONPATH myscript.py -o $@ ``` ### Stop using PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE directly {#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE} Specify Framework Compatibility Matrix Version in device manifest by adding a `target-level` attribute to the root element ``. If `PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE` is 26 or 27, you can add `"target-level"="1"` to your device manifest instead. ### Stop using USE_CLANG_PLATFORM_BUILD {#USE_CLANG_PLATFORM_BUILD} Clang is the default and only supported Android compiler, so there is no reason for this option to exist. ### Stop using clang property The clang property has been deleted from Soong. To fix any build errors, remove the clang property from affected Android.bp files using bpmodify. ``` make go run bpmodify.go -w -m=module_name -remove-property=true -property=clang filepath ``` `BUILD_BROKEN_CLANG_PROPERTY` can be used as temporarily workaround ### Stop using clang_cflags and clang_asflags clang_cflags and clang_asflags are deprecated. To fix any build errors, use bpmodify to either - move the contents of clang_asflags/clang_cflags into asflags/cflags or - delete clang_cflags/as_flags as necessary To Move the contents: ``` make go run bpmodify.go -w -m=module_name -move-property=true -property=clang_cflags -new-location=cflags filepath ``` To Delete: ``` make go run bpmodify.go -w -m=module_name -remove-property=true -property=clang_cflags filepath ``` `BUILD_BROKEN_CLANG_ASFLAGS` and `BUILD_BROKEN_CLANG_CFLAGS` can be used as temporarily workarounds ### Other envsetup.sh variables {#other_envsetup_variables} * ANDROID_TOOLCHAIN * ANDROID_TOOLCHAIN_2ND_ARCH * ANDROID_DEV_SCRIPTS * ANDROID_EMULATOR_PREBUILTS * ANDROID_PRE_BUILD_PATHS These are all exported from envsetup.sh, but don't have clear equivalents within the makefile system. If you need one of them, you'll have to set up your own version. ## Soong config variables ### Soong config string variables must list all values they can be set to In order to facilitate the transition to bazel, all soong_config_string_variables must only be set to a value listed in their `values` property, or an empty string. It is a build error otherwise. Example Android.bp: ``` soong_config_string_variable { name: "my_string_variable", values: [ "foo", "bar", ], } soong_config_module_type { name: "my_cc_defaults", module_type: "cc_defaults", config_namespace: "my_namespace", variables: ["my_string_variable"], properties: [ "shared_libs", "static_libs", ], } ``` Product config: ``` $(call soong_config_set,my_namespace,my_string_variable,baz) # Will be an error as baz is not listed in my_string_variable's values. ``` [build/soong/Changes.md]: https://android.googlesource.com/platform/build/soong/+/master/Changes.md [build/soong/docs/best_practices.md#headers]: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers [external/fonttools/Lib/fontTools/Android.bp]: https://android.googlesource.com/platform/external/fonttools/+/master/Lib/fontTools/Android.bp [frameworks/base/Android.bp]: https://android.googlesource.com/platform/frameworks/base/+/master/Android.bp [frameworks/base/data/fonts/Android.mk]: https://android.googlesource.com/platform/frameworks/base/+/master/data/fonts/Android.mk [hardware/interfaces/health/1.0/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/1.0/README.md [hardware/interfaces/health/2.1/README.md]: https://android.googlesource.com/platform/hardware/interfaces/+/master/health/2.1/README.md ================================================ FILE: CleanSpec.mk ================================================ # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # If you don't need to do a full clean build but would like to touch # a file or delete some intermediate files, add a clean step to the end # of the list. These steps will only be run once, if they haven't been # run before. # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) # $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) # # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with # files that are missing or have been moved. # # Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. # Use $(OUT_DIR) to refer to the "out" directory. # # If you need to re-do something that's already mentioned, just copy # the command and add it to the bottom of the list. E.g., if a change # that you made last week required touching a file and a change you # made today requires touching the same file, just copy the old # touch step and add it to the end of the list. # # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ # For example: #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmediaplayerservice_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libmedia_jni_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt) $(call add-clean-step, find $(PRODUCT_OUT) -name "*.apk" | xargs rm) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/*/LINKED) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/iself) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/lsd) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/apriori) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/isprelinked) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/soslim) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/*.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/*.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/YouTube*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_omx_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/librtp_jni_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/android-info.txt) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates) # ICS MR2!!!!!!!!!!!! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libbcinfo_intermediates) # WAIT, I MEAN JELLY BEAN!!!!!!!!!!!! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Changing where ro.carrier value is instantiated for system/build.prop $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Now we switched to build against Mac OS X SDK 10.6 $(call add-clean-step, rm -rf $(OUT_DIR)/host/darwin-x86/obj) $(call add-clean-step, rm -f $(OUT_DIR)/versions_checked.mk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) # JB MR2!!!!!!! AND *NO*, THIS WILL NOT BE K-WHATEVER. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Start of "K" development! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # GCC 4.7 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) # Wait, back to some JB development! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # And on to KLP... $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # KLP now based off API 18. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # Clean up around the /system/app -> /system/priv-app migration $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) # Clean up old location of generated Java files from aidl $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src) # Clean up ApplicationsProvider which is being removed. $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ApplicationsProvider_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/ApplicationsProvider.apk) # Clean up Moto OMA DM client which isn't ready yet. $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.dev_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin.diagmon_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.pluginhelper_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.plugin_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.omadm.service.api_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DMService_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SprintDM_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DMService.apk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/SprintDM.apk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/omadm) # GCC 4.8 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o) # KLP I mean KitKat now API 19. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # 4.4.1 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # 4.4.2 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # "L" and beyond. # Make libart the default runtime $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Rename persist.sys.dalvik.vm.lib to allow new default $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # KKWT development $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # L development $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # L development $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # Add ro.product.cpu.abilist{32,64} to build.prop. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Adding dalvik.vm.dex2oat-flags to eng builds $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Switching the x86 emulator over to a 64 bit primary zygote. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) # Rename persist.sys.dalvik.vm.lib.1 to allow new default $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Switching PRODUCT_RUNTIMES default for some devices $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Switching to 32-bit-by-default host multilib build $(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)) # KKWT has become API 20 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # ims-common.jar added to BOOTCLASSPATH $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/init.environ.rc_intermediates) # Change ro.zygote for core_64_bit.mk from zygote32_64 to zygote64_32 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # Adding dalvik.vm.dex2oat-Xms, dalvik.vm.dex2oat-Xmx # dalvik.vm.image-dex2oat-Xms, and dalvik.vm.image-dex2oat-Xmx $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system) # Switch host builds to Clang by default $(call add-clean-step, rm -rf $(OUT_DIR)/host) # Adding dalvik.vm.dex2oat-filter $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # API 21? $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # API 21! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # API 22! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # Move to libc++ as the default STL. $(call add-clean-step, rm -rf $(OUT_DIR)) # dex2oat instruction-set changes $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # Make GNU++11 the default standard version. This requires a cleanspec because # char16_t/char32_t will be real types now instead of typedefs, which means # an ABI change since the names will mangle differently. $(call add-clean-step, rm -rf $(OUT_DIR)) # 5.1! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # Remove ro.product.locale.language/country and add ro.product.locale # instead. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # On to MNC $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # Adding dalvik.vm.usejit $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # Rename dalvik.vm.usejit to debug.dalvik.vm.usejit $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # Revert rename dalvik.vm.usejit to debug.dalvik.vm.usejit $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # Change from interpret-only to verify-at-runtime. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) # New York, New York! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # 23 is becoming alive!!! $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) # Change PLATFORM_VERSION from NYC to N $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*) # $(PRODUCT_OUT)/recovery/root/sdcard goes from symlink to folder. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sdcard) # Add BOARD_USES_SYSTEM_OTHER_ODEX $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/previous_overlays.txt) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/current_packages.txt) $(call add-clean-step, rm -rf $(HOST_OUT_INTERMEDIATES)/include) $(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) $(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/previous_gen_java_config.mk) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/current_gen_java_config.mk) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*/package-res.apk) $(call add-clean-step, rm -rf $(TARGET_OUT_INTERMEDIATES)/APPS/*/package-res.apk) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) $(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES)) $(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES)) $(call add-clean-step, rm -rf $(TARGET_OUT_ETC)/init) # Libraries are moved from {system|vendor}/lib to ./lib/framework, ./lib/vndk, etc. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*) # Revert that move $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/lib*) # Sanitized libraries now live in a different location. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/lib*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/vendor/lib*) # Soong module variant change, remove obsolete intermediates $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates) # Version checking moving to Soong $(call add-clean-step, rm -rf $(OUT_DIR)/versions_checked.mk) # Vendor tests were being installed into /vendor/bin accidentally $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/nativetest*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/nativetest*) # Jack is no longer the default compiler, remove the intermediates $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/classes*.jack) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*/jack*) # Move adbd from $(PRODUCT_OUT)/root/sbin to $(PRODUCT_OUT)/system/bin $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/adbd) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/sbin/adbd) # Soong linux -> linux_glibc rename $(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_x86*' | xargs rm -rf) $(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -name 'linux_common*' | xargs rm -rf) # Remove old aidl/logtags files that may be in the generated source directory $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/*/*_intermediates/src) $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/*/*_intermediates/java-source-list) $(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/*/*_intermediates/src) $(call add-clean-step, rm -f $(OUT_DIR)/host/common/obj/*/*_intermediates/java-source-list) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*/flat-res) # Remove old VNDK directories without version $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk-sp) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/vndk-sp) # Remove old dex output directories $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/) $(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/with-local/) $(call add-clean-step, rm -rf $(HOST_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/no-local/) # Remove legacy VINTF metadata files $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/manifest.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/manifest.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/manifest.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/compatibility_matrix.xml) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/compatibility_matrix.xml) # Remove DisplayCutoutEmulation overlays $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationWide) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutoutEmulationNarrow) # Remove obsolete intermedates src files $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/*/*_intermediates/src/RenderScript.stamp*) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/java-source-list) $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/java-source-list) $(call add-clean-step, rm -rf $(OUT_DOCS)/*-timestamp) $(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/src) $(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/APPS/*_intermediates/java-source-list) $(call add-clean-step, rm -rf $(TARGET_COMMON_OUT_ROOT)/obj_asan/JAVA_LIBRARIES/*_intermediates/java-source-list) # Remove stale init.noenforce.rc $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/gsi/init.noenforce.rc) # Clean up Launcher3 which has been replaced with Launcher3QuickStep $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/Launcher3) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Launcher3) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Launcher3_intermediates) # Remove old merged AndroidManifest.xml location $(call add-clean-step, rm -rf $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS/*_intermediates/AndroidManifest.xml) $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "vr_hwc*" -print0 | xargs -0 rm -f) $(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/vold) # Remove product-services related files / images $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "*product-services*" -print0 | xargs -0 rm -rf) $(call add-clean-step, find $(PRODUCT_OUT) -type d -name "*product-services*" -print0 | xargs -0 rm -rf) $(call add-clean-step, find $(PRODUCT_OUT) -type l -name "*product-services*" -print0 | xargs -0 rm -rf) # Remove obsolete recovery etc files $(call add-clean-step, rm -rf $(TARGET_RECOVERY_ROOT_OUT)/etc) # Remove *_OUT_INTERMEDIATE_LIBRARIES $(call add-clean-step, rm -rf $(addsuffix /lib,\ $(HOST_OUT_INTERMEDIATES) $(2ND_HOST_OUT_INTERMEDIATES) \ $(HOST_CROSS_OUT_INTERMEDIATES) $(2ND_HOST_CROSS_OUT_INTERMEDIATES) \ $(TARGET_OUT_INTERMEDIATES) $(2ND_TARGET_OUT_INTERMEDIATES))) # Remove strip.sh intermediates to save space $(call add-clean-step, find $(OUT_DIR) \( -name "*.so.debug" -o -name "*.so.dynsyms" -o -name "*.so.funcsyms" -o -name "*.so.keep_symbols" -o -name "*.so.mini_debuginfo.xz" \) -print0 | xargs -0 rm -f) # Clean up old ninja files $(call add-clean-step, rm -f $(OUT_DIR)/build-*-dist*.ninja) $(call add-clean-step, rm -f $(HOST_OUT)/*ts/host-libprotobuf-java-*.jar) $(call add-clean-step, find $(OUT_DIR)/target/product/mainline_arm64/system -type f -name "*.*dex" -print0 | xargs -0 rm -f) # Clean up aidegen $(call add-clean-step, rm -f $(HOST_OUT)/bin/aidegen) # Remove perfprofd $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/perfprofd) # Remove incorrectly created directories in the source tree $(call add-clean-step, find system/app system/priv-app system/framework system_other -depth -type d -print0 | xargs -0 rmdir) $(call add-clean-step, rm -f .d) # Remove obsolete apps $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*) # Remove corrupt generated rule due to using toybox's sed $(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/system/core/init/generated_stub_builtin_function_map) # Clean up core JNI libraries moved to runtime apex $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libjavacore.so) $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libopenjdk.so) $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libexpat.so) # Merge product_services into product $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services) # Clean up old location of hiddenapi files $(call add-clean-step, rm -f $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi*) # Clean up previous default location of RROs $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay) # Remove ART artifacts installed only by modules `art-runtime` and # `art-tools` in /system on target. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm32) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dalvikvm64) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oat) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dex2oatd) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdiag) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexdump) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexlist) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzer) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/dexoptanalyzerd) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/oatdump) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profman) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/profmand) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnection.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libadbconnectiond.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-compiler.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-compiler.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-dexlayout.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd-dexlayout.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart-disassembler.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libart.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartd.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbase.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libartbased.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfiled.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_external.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdexfile_support.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_fd_forward.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libdt_socket.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjdwp.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkd.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvm.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmd.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmti.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libopenjdkjvmtid.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofile.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libprofiled.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libtombstoned_client.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixl.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libvixld.so) # Clean up old location of dexpreopted boot jars $(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/dex_bootjars_input) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libnpt.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*) # Clean up old testcase files $(call add-clean-step, rm -rf $(TARGET_OUT_TESTCASES)/*) $(call add-clean-step, rm -rf $(HOST_OUT_TESTCASES)/*) $(call add-clean-step, rm -rf $(HOST_CROSS_OUT_TESTCASES)/*) $(call add-clean-step, rm -rf $(TARGET_OUT_DATA)/*) $(call add-clean-step, rm -rf $(HOST_OUT)/vts/*) $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar) # Clean up old location of system_other.avbpubkey $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/security/avb/) # Clean up bufferhub files $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/hw/android.frameworks.bufferhub@1.0-service) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/android.frameworks.bufferhub@1.0-service.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/super.img) $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "generated_*_image_info.txt" -print0 | xargs -0 rm -f) # Clean up libicuuc.so and libicui18n.so $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libicu*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl) # Clean up adb_debug.propr $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/adb_debug.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libjavacrypto.so) # Clean up old verity tools. $(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/BootSignature.jar) $(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/VeritySigner.jar) $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/build_verity_metadata.py) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libc_malloc*) # Clean up old location of soft OMX plugins $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libstagefright_soft*) # Move odm build.prop to /odm/etc/. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/odm/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/odm/build.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex) # Remove libcameraservice and libcamera_client from base_system $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcameraservice.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libcamera_client.so) # Move product and system_ext to root for emulators $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product) $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext) # link_type and jni_link_type files are no longer needed $(call add-clean-step, find $(OUT_DIR) -type f -name "*link_type" -print0 | xargs -0 rm -f) # import_includes and export_includes files are no longer needed $(call add-clean-step, find $(OUT_DIR) -type f -name "import_includes" -o -name "export_includes" -print0 | xargs -0 rm -f) # Recreate product and system_ext partitions for emulator $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*product*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*system_ext*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product) $(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext) # Move GSI-specific files from /system to /system/system_ext $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/init.gsi.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/config/) # Move fuzz targets from /data/fuzz/* to /data/fuzz//* for device, and # /fuzz/* to /fuzz//* on host. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/fuzz/*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/data/fuzz/*) $(call add-clean-step, rm -rf $(HOST_OUT)/fuzz/*) $(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/host/*/fuzz/*) # Change file layout of system_other $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex) # Migrate preopt files to system_other for some devices $(call add-clean-step, rm -rf $(PRODUCT_OUT)/*/*app/*/oat) # Migrate preopt files from system_other for some devices $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other) # Remove Android Core Library artifacts from the system partition, now # that they live in the ART APEX (b/142944799). $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar) # Remove symlinks for VNDK apexes $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*) # Switch to symlinks for VNDK libs $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*) # Remove Android Core Library artifacts from the system partition # again, as the original change removing them was reverted. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar) # Remove cas@1.1 from the vendor partition $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@1.1*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.1*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.1*) # Remove com.android.cellbroadcast apex for Go devices $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.cellbroadcast.apex) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.cellbroadcast) # Remove CellBroadcastLegacyApp for Go devices $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CellBroadcastLegacyApp) # Remove MediaProvider after moving into APEX $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/MediaProvider) # The core image variant has been renamed to "" $(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_core*" -print0 | xargs -0 rm -rf) # Remove 'media' command $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/media) # Remove CtsShim apks from system partition, since the have been moved inside # the cts shim apex. Also remove the cts shim apex prebuilt since it has been # removed in flattened apexs configurations. $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CtsShimPrivPrebuilt) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CtsShimPrebuilt) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.apex.cts.shim.apex) # Remove vendor and recovery variants, the directory name has changed. $(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_recovery*" -print0 | xargs -0 rm -rf) $(call add-clean-step, find $(SOONG_OUT_DIR)/.intermediates -type d -name "android_*_vendor*" -print0 | xargs -0 rm -rf) # Remove PermissionController after moving into APEX $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/*PermissionController) # Clean up VTS-Core and VTS10 related artifacts. $(call add-clean-step, rm -rf $(HOST_OUT)/vts-core/*) $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-core-tradefed.jar) $(call add-clean-step, rm -rf $(HOST_OUT)/vts10/*) $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts10-tradefed.jar) # Clean up VTS again as VTS-Core will be renamed to VTS $(call add-clean-step, rm -rf $(HOST_OUT)/vts/*) $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/prop.default) # Workaround for Soong not being able to rebuild the host binary if its # JNI dependencies change: b/170389375 $(call add-clean-step, rm -rf $(OUT_DIR)/soong/host/*/lib*/libconscrypt_openjdk_jni.so) # vendor-ramdisk renamed to vendor_ramdisk $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor-ramdisk) # Common R directory has been removed. $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R) # Most of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES $(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) # More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES $(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) # More of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES $(call add-clean-step, rm -rf $(SOONG_HOST_OUT)) # Last of SOONG_HOST_OUT_EXECUTABLES has been moved to HOST_OUT_EXECUTABLES # Don't use SOONG_HOST_OUT, it is now an alias for HOST_OUT. $(call add-clean-step, rm -rf $(OUT_DIR)/soong/host) # Clear out tools/metalava Bazel output dir $(call add-clean-step, rm -rf $(OUT_DIR)/bazel/output/execroot/__main__/bazel-out/mixed_builds_product-*/bin/tools/metalava) # Clear out rustc compiler intermediates after reverting rust compiler/linker split. $(call add-clean-step, find $(OUT_DIR) -name "*.rsp.whole.a" -print0 | xargs -0 /bin/bash -c 'rm -f $$$${@}; rm -f $$$${@/.rsp.whole.a/.rsp.a}; rm -f $$$${@/.rsp.whole.a/.rsp}') # Remove obsolete java compilation artifacts $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/) $(call add-clean-step, find $(OUT_DIR) -type f -name "*.jar" -print0 | xargs -0 rm -f) # Remove obsolete java compilation artifacts $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/) $(call add-clean-step, find $(OUT_DIR) -type f -name "*.jar" -print0 | xargs -0 rm -f) # Remove obsolete dexpreopt_config artifacts $(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt.config) $(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt_soong.config) # Clear out Soong .intermediates directory regarding removal of hashed subdir $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ ================================================ FILE: Deprecation.md ================================================ # Deprecation of Make We've made significant progress converting AOSP from Make to Soong (Android.mk to Android.bp), and we're ready to start turning off pieces of Make. If you have any problems converting, please contact us via: * The [android-building@googlegroups.com] group. * Our [public bug tracker](https://issuetracker.google.com/issues/new?component=381517). * Or privately through your existing contacts at Google. ## Status [build/make/core/deprecation.mk] is the source of truth, but for easy browsing: | Module type | State | | -------------------------------- | --------- | | `BUILD_AUX_EXECUTABLE` | Obsolete | | `BUILD_AUX_STATIC_LIBRARY` | Obsolete | | `BUILD_COPY_HEADERS` | Error | | `BUILD_HOST_EXECUTABLE` | Error | | `BUILD_HOST_FUZZ_TEST` | Obsolete | | `BUILD_HOST_NATIVE_TEST` | Obsolete | | `BUILD_HOST_SHARED_LIBRARY` | Error | | `BUILD_HOST_SHARED_TEST_LIBRARY` | Obsolete | | `BUILD_HOST_STATIC_LIBRARY` | Error | | `BUILD_HOST_STATIC_TEST_LIBRARY` | Obsolete | | `BUILD_HOST_TEST_CONFIG` | Obsolete | | `BUILD_NATIVE_BENCHMARK` | Obsolete | | `BUILD_SHARED_TEST_LIBRARY` | Obsolete | | `BUILD_STATIC_TEST_LIBRARY` | Obsolete | | `BUILD_TARGET_TEST_CONFIG` | Obsolete | | `BUILD_*` | Available | ## Module Type Deprecation Process We'll be turning off `BUILD_*` module types as all of the users are removed from AOSP (and Google's internal trees). The process will go something like this, using `BUILD_PACKAGE` as an example: * Prerequisite: all common users of `BUILD_PACKAGE` have been removed (some device-specific ones may remain). * `BUILD_PACKAGE` will be moved from `AVAILABLE_BUILD_MODULE_TYPES` to `DEFAULT_WARNING_BUILD_MODULE_TYPES` in [build/make/core/deprecation.mk]. This will make referring to `BUILD_PACKAGE` a warning. * Any devices that still have warnings will have `BUILD_BROKEN_USES_BUILD_PACKAGE := true` added to their `BoardConfig.mk`. * `BUILD_PACKAGE` will be switched from `DEFAULT_WARNING_BUILD_MODULE_TYPES` to `DEFAULT_ERROR_BUILD_MODULE_TYPES`, which will turn referring to `BUILD_PACKAGE` into an error unless the device has overridden it. * At some later point, after all devices in AOSP no longer set `BUILD_BROKEN_USES_BUILD_PACKAGE`, `BUILD_PACKAGE` will be moved from `DEFAULT_ERROR_BUILD_MODULE_TYPES` to `OBSOLETE_BUILD_MODULE_TYPES` and the code will be removed. It will no longer be possible to use `BUILD_PACKAGE`. In most cases, we expect module types to stay in the default warning state for about two weeks before becoming an error by default. Then it will spend some amount of time in the default error state before moving to obsolete -- we'll try and keep that around for a while, but other development may cause those to break, and the fix may to be to obsolete them. There is no expectation that the `BUILD_BROKEN_USES_BUILD_*` workarounds will work in a future release, it's a short-term workaround. Just to be clear, the above process will happen on the AOSP master branch. So if you're following Android releases, none of the deprecation steps will be in Android Q, and the 2020 release will have jumped directly to the end for many module types. [android-building@googlegroups.com]: https://groups.google.com/forum/#!forum/android-building [build/make/core/deprecation.mk]: /core/deprecation.mk ================================================ FILE: OWNERS ================================================ include platform/build/soong:/OWNERS # Since this file affects all Android developers, lock it down. There is still # round the world timzeone coverage. per-file envsetup.sh = joeo@google.com, jingwen@google.com per-file shell_utils.sh = joeo@google.com, jingwen@google.com ================================================ FILE: PREUPLOAD.cfg ================================================ [Hook Scripts] do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT} [Builtin Hooks] ktfmt = true ================================================ FILE: README.md ================================================ # Android Make Build System This is the Makefile-based portion of the Android Build System. For documentation on how to run a build, see [Usage.txt](Usage.txt) For a list of behavioral changes useful for Android.mk writers see [Changes.md](Changes.md) For an outdated reference on Android.mk files, see [build-system.html](/core/build-system.html). Our Android.mk files look similar, but are entirely different from the Android.mk files used by the NDK build system. When searching for documentation elsewhere, ensure that it is for the platform build system -- most are not. This Makefile-based system is in the process of being replaced with [Soong], a new build system written in Go. During the transition, all of these makefiles are read by [Kati], and generate a ninja file instead of being executed directly. That's combined with a ninja file read by Soong so that the build graph of the two systems can be combined and run as one. [Kati]: https://github.com/google/kati [Soong]: https://android.googlesource.com/platform/build/soong/+/master ================================================ FILE: Usage.txt ================================================ Android build system usage: m [-j] [] [=...] Ways to specify what to build: The common way to specify what to build is to set that information in the environment via: # Set up the shell environment. source build/envsetup.sh # Run "hmm" after sourcing for more info # Select the device and variant to target. If no argument is given, it # will list choices and prompt. lunch [-] # Selects the device and variant to target. # Invoke the configured build. m [] [] [=...] is the device that the created image is intended to be run on. This is saved in the shell environment as $TARGET_PRODUCT by `lunch`. is one of "user", "userdebug", or "eng", and controls the amount of debugging to be added into the generated image. This gets saved in the shell environment as $TARGET_BUILD_VARIANT by `lunch`. Each of , , and = is optional. If no targets are specified, the build system will build the images for the configured product and variant. A target may be a file path. For example, out/host/linux-x86/bin/adb . Note that when giving a relative file path as a target, that path is interpreted relative to the root of the source tree (rather than relative to the current working directory). A target may also be any other target defined within a Makefile. Run `m help` to view the names of some common targets. To view the modules and targets defined in a particular directory, look for: files named *.mk (most commonly Android.mk) these files are defined in Make syntax files named Android.bp these files are defined in Blueprint syntax During a build, a few log files are generated in ${OUT} (or ${DIST_DIR}/logs for dist builds): verbose.log.gz every command run, along with its outputs. This is similar to the previous `m showcommands` option. error.log list of actions that failed during the build, and their outputs. soong.log verbose debug information from soong_ui For now, the full (extremely large) compiled list of targets can be found (after running the build once), split among these two files: ${OUT}/build-*.ninja ${OUT}/soong/build.ninja If you find yourself interacting with these files, you are encouraged to provide a more convenient tool for browsing targets, and to mention the tool here. Targets that adjust an existing build: dist Copy into ${DIST_DIR} the portion of the build that must be distributed Flags -j Run processes at once -j Autodetect the number of processes to run at once, and run that many Variables Variables can either be set in the surrounding shell environment or can be passed as command-line arguments. For example: export I_AM_A_SHELL_VAR=1 I_AM_ANOTHER_SHELL_VAR=2 m droid I_AM_A_MAKE_VAR=3 Here are some common variables and their meanings: TARGET_PRODUCT The to build # as described above TARGET_BUILD_VARIANT The to build # as described above DIST_DIR The directory in which to place the distribution artifacts. OUT_DIR The directory in which to place non-distribution artifacts. There is not yet known a convenient method by which to discover the full list of supported variables. Please mention it here when there is. ================================================ FILE: backported_fixes/Android.bp ================================================ // Copyright 2024 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], default_team: "trendy_team_android_media_reliability", } genrule { name: "applied_backported_fixes", tools: ["applied_backported_fixes_property_writer"], srcs: [":applied_backported_fix_binpbs"], out: ["applied_backported_fixes.prop"], cmd: "$(location applied_backported_fixes_property_writer)" + " -p $(location applied_backported_fixes.prop)" + " $(in)", } filegroup { name: "backported_fixes_proto_file", srcs: [ "backported_fixes.proto", ], } java_library { name: "backported_fixes_proto", srcs: ["backported_fixes.proto"], host_supported: true, sdk_version: "current", } java_library { name: "backported_fixes_common", srcs: ["src/java/com/android/build/backportedfixes/common/*.java"], static_libs: [ "backported_fixes_proto", "guava", ], host_supported: true, } java_test_host { name: "backported_fixes_common_test", srcs: ["tests/java/com/android/build/backportedfixes/common/*.java"], static_libs: [ "backported_fixes_common", "backported_fixes_proto", "junit", "truth", "truth-liteproto-extension", "truth-proto-extension", ], test_options: { unit_test: true, }, test_suites: ["general-tests"], } java_library { name: "backported_fixes_main_lib", srcs: ["src/java/com/android/build/backportedfixes/*.java"], static_libs: [ "backported_fixes_common", "backported_fixes_proto", "jcommander", "guava", ], host_supported: true, } java_binary_host { name: "applied_backported_fixes_property_writer", main_class: "com.android.build.backportedfixes.WriteBackportedFixesPropFile", static_libs: [ "backported_fixes_main_lib", ], } java_binary_host { name: "backported_fixes_combiner", main_class: "com.android.build.backportedfixes.CombineBackportedFixes", static_libs: [ "backported_fixes_main_lib", ], } // Combines BackportedFix binary proto files into a single BackportedFixes binary proto file. genrule_defaults { name: "default_backported_fixes_combiner", tools: ["backported_fixes_combiner"], cmd: "$(location backported_fixes_combiner)" + " -o $(out)" + " $(in)", } java_test_host { name: "backported_fixes_main_lib_test", srcs: ["tests/java/com/android/build/backportedfixes/*.java"], static_libs: [ "backported_fixes_main_lib", "backported_fixes_proto", "junit", "truth", ], test_options: { unit_test: true, }, test_suites: ["general-tests"], } // Converts BackprotedFix text protos to binary protos genrule_defaults { name: "default_backported_fix_binpbs", tools: ["aprotoc"], tool_files: [ ":backported_fixes_proto_file", ], cmd: "$(location aprotoc) " + " --encode=com.android.build.backportedfixes.BackportedFix" + " $(location :backported_fixes_proto_file)" + " < $(in)" + " > $(out); echo $(out)", } gensrcs { name: "applied_backported_fix_binpbs", defaults: ["default_backported_fix_binpbs"], output_extension: "binpb", srcs: [ "applied_fixes/*.txtpb", ], } ================================================ FILE: backported_fixes/OWNERS ================================================ essick@google.com nchalko@google.com portmannc@google.com ================================================ FILE: backported_fixes/applied_fixes/ki350037023.txtpb ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # proto-file: ../backported_fixes.proto # proto-message: BackportedFix known_issue: 350037023 alias: 1 ================================================ FILE: backported_fixes/backported_fixes.proto ================================================ // Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto2"; package com.android.build.backportedfixes; option java_multiple_files = true; // A list of backported fixes. message BackportedFixes { repeated BackportedFix fixes = 1; } // A known issue approved for reporting Build.getBackportedFixStatus message BackportedFix { // The issue id from the public bug tracker // https://issuetracker.google.com/issues/{known_issue} optional int64 known_issue = 1; // The alias for the known issue. // 1 - 1023 are valid aliases // Must be unique across all backported fixes. optional int32 alias = 2; } ================================================ FILE: backported_fixes/src/java/com/android/build/backportedfixes/CombineBackportedFixes.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes; import com.android.build.backportedfixes.common.Parser; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.converters.FileConverter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; /** Creates a BackportedFixes binary proto file from a list of BackportedFix proto binary files. */ public final class CombineBackportedFixes { @Parameter(description = "BackportedFix proto binary files", converter = FileConverter.class, required = true) List fixFiles; @Parameter(description = "Write the BackportedFixes proto binary to this file", names = {"--out","-o"}, converter = FileConverter.class, required = true) File outFile; public static void main(String... argv) throws Exception { CombineBackportedFixes main = new CombineBackportedFixes(); JCommander.newBuilder().addObject(main).build().parse(argv); main.run(); } CombineBackportedFixes() { } private void run() throws Exception { try (var out = new FileOutputStream(outFile)) { var fixes = Parser.parseBackportedFixFiles(fixFiles); writeBackportedFixes(fixes, out); } } static void writeBackportedFixes(BackportedFixes fixes, OutputStream out) throws IOException { fixes.writeTo(out); } } ================================================ FILE: backported_fixes/src/java/com/android/build/backportedfixes/WriteBackportedFixesPropFile.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes; import static java.nio.charset.StandardCharsets.UTF_8; import com.android.build.backportedfixes.common.Parser; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.converters.FileConverter; import com.google.common.io.Files; import java.io.File; import java.io.PrintWriter; import java.io.Writer; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Creates backported fix properties file. * *

Writes BitSet of backported fix aliases from a list of BackportedFix proto binary files and * writes the property {@value PROPERTY_NAME} to a file. */ public final class WriteBackportedFixesPropFile { private static final String PROPERTY_NAME = "ro.build.backported_fixes.alias_bitset.long_list"; @Parameter(description = "BackportedFix proto binary files", converter = FileConverter.class, required = true) List fixFiles; @Parameter(description = "The file to write the property value to.", names = {"--property_file", "-p"}, converter = FileConverter.class, required = true) File propertyFile; public static void main(String... argv) throws Exception { WriteBackportedFixesPropFile main = new WriteBackportedFixesPropFile(); JCommander.newBuilder().addObject(main).build().parse(argv); main.run(); } WriteBackportedFixesPropFile() { } private void run() throws Exception { try (var out = Files.newWriter(propertyFile, UTF_8)) { var fixes = Parser.parseBackportedFixFiles(fixFiles); writeFixesAsAliasBitSet(fixes, out); } } static void writeFixesAsAliasBitSet(BackportedFixes fixes, Writer out) { PrintWriter printWriter = new PrintWriter(out); printWriter.println("# The following backported fixes have been applied"); for (var f : fixes.getFixesList()) { printWriter.printf("# https://issuetracker.google.com/issues/%d with alias %d", f.getKnownIssue(), f.getAlias()); printWriter.println(); } var bsArray = Parser.getBitSetArray( fixes.getFixesList().stream().mapToInt(BackportedFix::getAlias).toArray()); String bsString = Arrays.stream(bsArray).mapToObj(Long::toString).collect( Collectors.joining(",")); printWriter.printf("%s=%s", PROPERTY_NAME, bsString); printWriter.println(); if (printWriter.checkError()) { throw new RuntimeException("There was an error writing to " + out.toString()); } } } ================================================ FILE: backported_fixes/src/java/com/android/build/backportedfixes/common/Parser.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes.common; import static com.google.common.base.Preconditions.checkNotNull; import com.android.build.backportedfixes.BackportedFix; import com.android.build.backportedfixes.BackportedFixes; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.BitSet; import java.util.Comparator; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; /** Static utilities for working with {@link BackportedFixes}. */ public final class Parser { /** Creates list of FileInputStreams for a list of files. */ public static ImmutableList getFileInputStreams(List fixFiles) throws FileNotFoundException { var streams = ImmutableList.builder(); for (var f : fixFiles) { streams.add(new FileInputStream(f)); } return streams.build(); } /** Converts a list of backported fix aliases into a long array representing a {@link BitSet} */ public static long[] getBitSetArray(int[] aliases) { BitSet bs = new BitSet(); for (int a : aliases) { bs.set(a); } return bs.toLongArray(); } /** * Creates a {@link BackportedFixes} from a list of {@link BackportedFix} binary proto streams. */ public static BackportedFixes parseBackportedFixFiles(List fixFiles) throws IOException { try { return fixFiles.stream().map(Parser::tunelFileInputStream) .map(Parser::tunnelParse) .sorted(Comparator.comparing(BackportedFix::getKnownIssue)) .collect(fixCollector()); } catch (TunnelException e) { throw e.rethrow(FileNotFoundException.class, IOException.class); } } private static Collector fixCollector() { return Collectors.collectingAndThen(Collectors.toList(), fixList -> { var result = BackportedFixes.newBuilder(); result.addAllFixes(fixList); return result.build(); }); } private static FileInputStream tunelFileInputStream(File file) throws TunnelException { try { return new FileInputStream(file); } catch (FileNotFoundException e) { throw new TunnelException(e); } } private static BackportedFix tunnelParse(InputStream s) throws TunnelException { try { var fix = BackportedFix.parseFrom(s); s.close(); return fix; } catch (IOException e) { throw new TunnelException(e); } } private static class TunnelException extends RuntimeException { TunnelException(Exception cause) { super("If you see this TunnelException something went wrong. It should always be rethrown as the cause.", cause); } RuntimeException rethrow(Class exceptionClazz) throws X { checkNotNull(exceptionClazz); Throwables.throwIfInstanceOf(getCause(), exceptionClazz); throw exception( getCause(), "rethrow(%s) doesn't match underlying exception", exceptionClazz); } public RuntimeException rethrow( Class exceptionClazz1, Class exceptionClazz2) throws X1, X2 { checkNotNull(exceptionClazz1); checkNotNull(exceptionClazz2); Throwables.throwIfInstanceOf(getCause(), exceptionClazz1); Throwables.throwIfInstanceOf(getCause(), exceptionClazz2); throw exception( getCause(), "rethrow(%s, %s) doesn't match underlying exception", exceptionClazz1, exceptionClazz2); } private static ClassCastException exception( Throwable cause, String message, Object... formatArgs) { ClassCastException result = new ClassCastException(String.format(message, formatArgs)); result.initCause(cause); return result; } } private Parser() { } } ================================================ FILE: backported_fixes/tests/java/com/android/build/backportedfixes/CombineBackportedFixesTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes; import com.google.common.truth.Truth; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; /** Tests for {@link CombineBackportedFixes}. */ public class CombineBackportedFixesTest { @Test public void writeBackportedFixes_default() throws IOException { // Not much of a test, but there is not much to test. BackportedFixes fixes = BackportedFixes.newBuilder() .addFixes(BackportedFix.newBuilder().setKnownIssue(123).build()) .build(); var result = new ByteArrayOutputStream(); CombineBackportedFixes.writeBackportedFixes(fixes, result); Truth.assertThat(BackportedFixes.parseFrom(result.toByteArray())) .isEqualTo(fixes); } } ================================================ FILE: backported_fixes/tests/java/com/android/build/backportedfixes/WriteBackportedFixesPropFileTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes; import com.google.common.truth.Truth; import org.junit.Test; import java.io.PrintWriter; import java.io.StringWriter; /** Tests for {@link WriteBackportedFixesPropFile}. */ public class WriteBackportedFixesPropFileTest { @Test public void writeFixesAsAliasBitSet_default() { BackportedFixes fixes = BackportedFixes.newBuilder().build(); var result = new StringWriter(); WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result)); Truth.assertThat(result.toString()) .isEqualTo(""" # The following backported fixes have been applied ro.build.backported_fixes.alias_bitset.long_list= """); } @Test public void writeFixesAsAliasBitSet_some() { BackportedFixes fixes = BackportedFixes.newBuilder() .addFixes(BackportedFix.newBuilder().setKnownIssue(1234L).setAlias(1)) .addFixes(BackportedFix.newBuilder().setKnownIssue(3L).setAlias(65)) .addFixes(BackportedFix.newBuilder().setKnownIssue(4L).setAlias(67)) .build(); var result = new StringWriter(); WriteBackportedFixesPropFile.writeFixesAsAliasBitSet(fixes, new PrintWriter(result)); Truth.assertThat(result.toString()) .isEqualTo(""" # The following backported fixes have been applied # https://issuetracker.google.com/issues/1234 with alias 1 # https://issuetracker.google.com/issues/3 with alias 65 # https://issuetracker.google.com/issues/4 with alias 67 ro.build.backported_fixes.alias_bitset.long_list=2,10 """); } } ================================================ FILE: backported_fixes/tests/java/com/android/build/backportedfixes/common/ParserTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.build.backportedfixes.common; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; import com.android.build.backportedfixes.BackportedFix; import com.android.build.backportedfixes.BackportedFixes; import com.google.common.collect.ImmutableList; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; /** Tests for {@link Parser}.*/ public class ParserTest { @Rule public TemporaryFolder mTempFolder = new TemporaryFolder(); @Test public void getFileInputStreams() throws IOException { var results = Parser.getFileInputStreams( ImmutableList.of(Files.createTempFile("test", null).toFile())); assertThat(results).isNotEmpty(); } @Test public void getBitSetArray_empty() { var results = Parser.getBitSetArray(new int[]{}); assertThat(results).isEmpty(); } @Test public void getBitSetArray_2_3_64() { var results = Parser.getBitSetArray(new int[]{2,3,64}); assertThat(results).asList().containsExactly(12L,1L).inOrder(); } @Test public void parseBackportedFixFiles_empty() throws IOException { var result = Parser.parseBackportedFixFiles(ImmutableList.of()); assertThat(result).isEqualTo(BackportedFixes.getDefaultInstance()); } @Test public void parseBackportedFixFiles_oneBlank() throws IOException { var result = Parser.parseBackportedFixFiles(ImmutableList.of(mTempFolder.newFile())); assertThat(result).isEqualTo( BackportedFixes.newBuilder() .addFixes(BackportedFix.getDefaultInstance()) .build()); } @Test public void parseBackportedFixFiles_two() throws IOException { BackportedFix ki123 = BackportedFix.newBuilder() .setKnownIssue(123) .setAlias(1) .build(); BackportedFix ki456 = BackportedFix.newBuilder() .setKnownIssue(456) .setAlias(2) .build(); var result = Parser.parseBackportedFixFiles( ImmutableList.of(tempFile(ki456), tempFile(ki123))); assertThat(result).isEqualTo( BackportedFixes.newBuilder() .addFixes(ki123) .addFixes(ki456) .build()); } private File tempFile(BackportedFix fix) throws IOException { File f = mTempFolder.newFile(); try (FileOutputStream out = new FileOutputStream(f)) { fix.writeTo(out); return f; } } } ================================================ FILE: banchanHelp.sh ================================================ #!/bin/bash # locate some directories cd "$(dirname $0)" SCRIPT_DIR="${PWD}" cd ../.. TOP="${PWD}" message='usage: banchan ... [|arm|x86|arm64|x86_64] [eng|userdebug|user] banchan selects individual APEX modules to be built by the Android build system. Like "tapas", "banchan" does not request the building of images for a device but instead configures it for an unbundled build of the given modules, suitable for installing on any api-compatible device. The difference from "tapas" is that "banchan" sets the appropriate products etc for building APEX modules rather than apps (APKs). The module names should match apex{} modules in Android.bp files, typically starting with "com.android.". The product argument should be a product name ending in "_", where is one of arm, x86, arm64, x86_64. It can also be just an arch, in which case the standard product for building modules with that architecture is used, i.e. module_. The usage of the other arguments matches that of the rest of the platform build system and can be found by running `m help`' echo "$message" ================================================ FILE: buildspec.mk.default ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ###################################################################### # This is a do-nothing template file. To use it, copy it to a file # named "buildspec.mk" in the root directory, and uncomment or change # the variables necessary for your desired configuration. The file # "buildspec.mk" should never be checked in to source control. ###################################################################### # Choose a product to build for. Look in the products directory for ones # that work. ifndef TARGET_PRODUCT #TARGET_PRODUCT:=generic endif # Choose a variant to build. If you don't pick one, the default is eng. # User is what we ship. Userdebug is that, with a few flags turned on # for debugging. Eng has lots of extra tools for development. ifndef TARGET_BUILD_VARIANT #TARGET_BUILD_VARIANT:=user #TARGET_BUILD_VARIANT:=userdebug #TARGET_BUILD_VARIANT:=eng endif # Choose a targeted release. If you don't pick one, the default is the # soonest future release. ifndef TARGET_PLATFORM_RELEASE #TARGET_PLATFORM_RELEASE:=OPR1 endif # Choose additional targets to always install, even when building # minimal targets like "make droid". This takes simple target names # like "Browser" or "MyApp", the names used by LOCAL_MODULE or # LOCAL_PACKAGE_NAME. Modules listed here will always be installed in # /system, even if they'd usually go in /data. ifndef CUSTOM_MODULES #CUSTOM_MODULES:= endif # Set this to debug or release if you care. Otherwise, it defaults to release. ifndef TARGET_BUILD_TYPE #TARGET_BUILD_TYPE:=release endif # Uncomment this if you want the host tools built in debug mode. Otherwise # it defaults to release. ifndef HOST_BUILD_TYPE #HOST_BUILD_TYPE:=debug endif # Turn on debugging for selected modules. If DEBUG_MODULE_ is set # to a non-empty value, the appropriate HOST_/TARGET_CUSTOM_DEBUG_CFLAGS # will be added to LOCAL_CFLAGS when building the module. #DEBUG_MODULE_ModuleName:=true # Specify the extra CFLAGS to use when building a module whose # DEBUG_MODULE_ variable is set. Host and device flags are handled # separately. #HOST_CUSTOM_DEBUG_CFLAGS:= #TARGET_CUSTOM_DEBUG_CFLAGS:= # Choose additional locales, like "en_US" or "it_IT", to add to any # built product. Any locales that appear in CUSTOM_LOCALES but not in # the locale list for the selected product will be added to the end # of PRODUCT_LOCALES. ifndef CUSTOM_LOCALES #CUSTOM_LOCALES:= endif # If you have a special place to put your ouput files, set this, otherwise # it goes to /out #OUT_DIR:=/tmp/stuff # If you want to always set certain system properties, add them to this list. # E.g., "ADDITIONAL_BUILD_PROPERTIES += ro.prop1=5 prop2=value" # This mechanism does not currently support values containing spaces. #ADDITIONAL_BUILD_PROPERTIES += # If you want to reduce the system.img size by several meg, and are willing to # lose access to CJK (and other) character sets, define NO_FALLBACK_FONT:=true ifndef NO_FALLBACK_FONT #NO_FALLBACK_FONT:=true endif # OVERRIDE_RUNTIMES allows you to locally override PRODUCT_RUNTIMES. # # To only build ART, use "runtime_libart_default" # To use Dalvik but also include ART, use "runtime_libdvm_default runtime_libart" # To use ART but also include Dalvik, use "runtime_libart_default runtime_libdvm" ifndef OVERRIDE_RUNTIMES #OVERRIDE_RUNTIMES:=runtime_libart_default #OVERRIDE_RUNTIMES:=runtime_libdvm_default runtime_libart #OVERRIDE_RUNTIMES:=runtime_libart_default runtime_libdvm endif # when the build system changes such that this file must be updated, this # variable will be changed. After you have modified this file with the new # changes (see buildspec.mk.default), update this to the new value from # buildspec.mk.default. BUILD_ENV_SEQUENCE_NUMBER := 13 ================================================ FILE: ci/Android.bp ================================================ // Copyright 2024 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], default_team: "trendy_team_adte", } python_test_host { name: "build_test_suites_test", main: "build_test_suites_test.py", pkg_path: "testdata", srcs: [ "build_test_suites_test.py", ], libs: [ "build_test_suites_lib", "pyfakefs", "ci_test_lib", ], test_options: { unit_test: true, }, data: [ ":py3-cmd", ], } // This test is only intended to be run locally since it's slow, not hermetic, // and requires a lot of system state. It is therefore not marked as `unit_test` // and is not part of any test suite. Note that we also don't want to run this // test with Bazel since that would require disabling sandboxing and explicitly // passing in all the env vars we depend on via the command-line. The test // target could be configured to do so but it's not worth doing seeing that // we're moving away from Bazel. python_test_host { name: "build_test_suites_local_test", main: "build_test_suites_local_test.py", srcs: [ "build_test_suites_local_test.py", ], libs: [ "build_test_suites_lib", "pyfakefs", "ci_test_lib", ], test_config_template: "AndroidTest.xml.template", test_options: { unit_test: false, }, } python_test_host { name: "optimized_targets_test", main: "optimized_targets_test.py", pkg_path: "testdata", srcs: [ "optimized_targets_test.py", ], libs: [ "build_test_suites_lib", "pyfakefs", ], test_options: { unit_test: true, }, data: [ ":py3-cmd", ], } python_binary_host { name: "build_test_suites", srcs: [ "build_test_suites.py", "optimized_targets.py", "test_mapping_module_retriever.py", "build_context.py", "test_discovery_agent.py", "metrics_agent.py", "buildbot.py", ], main: "build_test_suites.py", libs: [ "soong-metrics-proto-py", ], } python_library_host { name: "build_test_suites_lib", srcs: [ "build_test_suites.py", "optimized_targets.py", "test_mapping_module_retriever.py", "build_context.py", "test_discovery_agent.py", "metrics_agent.py", "buildbot.py", ], libs: [ "soong-metrics-proto-py", ], } python_library_host { name: "ci_test_lib", srcs: [ "ci_test_lib.py", ], } ================================================ FILE: ci/AndroidTest.xml.template ================================================ ================================================ FILE: ci/build_context.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Container class for build context with utility functions.""" import re class BuildContext: def __init__(self, build_context_dict: dict[str, any]): self.enabled_build_features = set() for opt in build_context_dict.get('enabledBuildFeatures', []): self.enabled_build_features.add(opt.get('name')) self.test_infos = set() for test_info_dict in build_context_dict.get('testContext', dict()).get( 'testInfos', [] ): self.test_infos.add(self.TestInfo(test_info_dict)) def build_target_used(self, target: str) -> bool: return any(test.build_target_used(target) for test in self.test_infos) class TestInfo: _DOWNLOAD_OPTS = { 'test-config-only-zip', 'test-zip-file-filter', 'extra-host-shared-lib-zip', 'sandbox-tests-zips', 'additional-files-filter', 'cts-package-name', } def __init__(self, test_info_dict: dict[str, any]): self.is_test_mapping = False self.test_mapping_test_groups = set() self.file_download_options = set() self.name = test_info_dict.get('name') self.command = test_info_dict.get('command') self.extra_options = test_info_dict.get('extraOptions') for opt in test_info_dict.get('extraOptions', []): key = opt.get('key') if key == 'test-mapping-test-group': self.is_test_mapping = True self.test_mapping_test_groups.update(opt.get('values', set())) if key in self._DOWNLOAD_OPTS: self.file_download_options.update(opt.get('values', set())) def build_target_used(self, target: str) -> bool: # For all of a targets' outputs, check if any of the regexes used by tests # to download artifacts would match it. If any of them do then this target # is necessary. regex = r'\b(%s)\b' % re.escape(target) return any(re.search(regex, opt) for opt in self.file_download_options) ================================================ FILE: ci/build_device_and_tests ================================================ #!/usr/bin/env bash # # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -euo pipefail build/soong/soong_ui.bash --make-mode build_test_suites $(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites --device-build $@ ================================================ FILE: ci/build_metadata ================================================ #/bin/bash # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -x source build/make/shell_utils.sh export TARGET_PRODUCT=aosp_arm64 export TARGET_RELEASE=trunk_staging export TARGET_BUILD_VARIANT=eng import_build_vars \ OUT_DIR \ DIST_DIR \ HOST_OUT_EXECUTABLES \ || exit $? TARGETS=( all_teams source_tree_size release_config_metadata ) # Build modules build/soong/bin/m dist ${TARGETS[@]} || exit $? # List all source files in the tree ( \ $HOST_OUT_EXECUTABLES/source_tree_size -o $DIST_DIR/all_source_tree_files.pb \ && gzip -fn $DIST_DIR/all_source_tree_files.pb \ ) || exit $? ================================================ FILE: ci/build_test_suites ================================================ #!/usr/bin/env bash # # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -euo pipefail build/soong/soong_ui.bash --make-mode build_test_suites $(build/soong/soong_ui.bash --dumpvar-mode HOST_OUT)/bin/build_test_suites $@ ================================================ FILE: ci/build_test_suites.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Build script for the CI `test_suites` target.""" import argparse from dataclasses import dataclass import json import logging import os import pathlib import re import subprocess import sys from typing import Callable from build_context import BuildContext import optimized_targets import metrics_agent import test_discovery_agent REQUIRED_ENV_VARS = frozenset(['TARGET_PRODUCT', 'TARGET_RELEASE', 'TOP', 'DIST_DIR']) SOONG_UI_EXE_REL_PATH = 'build/soong/soong_ui.bash' LOG_PATH = 'logs/build_test_suites.log' # Currently, this prevents the removal of those tags when they exist. In the future we likely # want the script to supply 'dist directly REQUIRED_BUILD_TARGETS = frozenset(['dist', 'droid', 'checkbuild']) class Error(Exception): def __init__(self, message): super().__init__(message) class BuildFailureError(Error): def __init__(self, return_code): super().__init__(f'Build command failed with return code: f{return_code}') self.return_code = return_code class BuildPlanner: """Class in charge of determining how to optimize build targets. Given the build context and targets to build it will determine a final list of targets to build along with getting a set of packaging functions to package up any output zip files needed by the build. """ def __init__( self, build_context: BuildContext, args: argparse.Namespace, target_optimizations: dict[str, optimized_targets.OptimizedBuildTarget], ): self.build_context = build_context self.args = args self.target_optimizations = target_optimizations def create_build_plan(self): if 'optimized_build' not in self.build_context.enabled_build_features: return BuildPlan(set(self.args.extra_targets), set()) if not self.build_context.test_infos: logging.warning('Build context has no test infos, skipping optimizations.') for target in self.args.extra_targets: get_metrics_agent().report_unoptimized_target(target, 'BUILD_CONTEXT has no test infos.') return BuildPlan(set(self.args.extra_targets), set()) build_targets = set() packaging_commands_getters = [] # In order to roll optimizations out differently between test suites and # device builds, we have separate flags. enable_discovery = (('test_suites_zip_test_discovery' in self.build_context.enabled_build_features and not self.args.device_build ) or ( 'device_zip_test_discovery' in self.build_context.enabled_build_features and self.args.device_build )) and not self.args.test_discovery_info_mode logging.info(f'Discovery mode is enabled= {enable_discovery}') preliminary_build_targets = self._collect_preliminary_build_targets(enable_discovery) for target in preliminary_build_targets: target_optimizer_getter = self.target_optimizations.get(target, None) if not target_optimizer_getter: build_targets.add(target) continue target_optimizer = target_optimizer_getter( target, self.build_context, self.args ) build_targets.update(target_optimizer.get_build_targets()) packaging_commands_getters.append( target_optimizer.get_package_outputs_commands ) return BuildPlan(build_targets, packaging_commands_getters) def _collect_preliminary_build_targets(self, enable_discovery: bool): build_targets = set() try: test_discovery_zip_regexes = self._get_test_discovery_zip_regexes() logging.info(f'Discovered test discovery regexes: {test_discovery_zip_regexes}') except test_discovery_agent.TestDiscoveryError as e: optimization_rationale = e.message logging.warning(f'Unable to perform test discovery: {optimization_rationale}') for target in self.args.extra_targets: get_metrics_agent().report_unoptimized_target(target, optimization_rationale) return self._legacy_collect_preliminary_build_targets() for target in self.args.extra_targets: if target in REQUIRED_BUILD_TARGETS: build_targets.add(target) get_metrics_agent().report_unoptimized_target(target, 'Required build target.') continue # If nothing is discovered without error, that means nothing is needed. if not test_discovery_zip_regexes: get_metrics_agent().report_optimized_target(target) continue regex = r'\b(%s.*)\b' % re.escape(target) for opt in test_discovery_zip_regexes: try: if re.search(regex, opt): get_metrics_agent().report_unoptimized_target(target, 'Test artifact used.') build_targets.add(target) # proceed to next target evaluation break get_metrics_agent().report_optimized_target(target) except Exception as e: # In case of exception report as unoptimized build_targets.add(target) get_metrics_agent().report_unoptimized_target(target, f'Error in parsing test discovery output for {target}: {repr(e)}') logging.error(f'unable to parse test discovery output: {repr(e)}') break # If discovery is not enabled, return the original list if not enable_discovery: return self._legacy_collect_preliminary_build_targets() return build_targets def _legacy_collect_preliminary_build_targets(self): build_targets = set() for target in self.args.extra_targets: if self._unused_target_exclusion_enabled( target ) and not self.build_context.build_target_used(target): continue build_targets.add(target) return build_targets def _unused_target_exclusion_enabled(self, target: str) -> bool: return ( f'{target}_unused_exclusion' in self.build_context.enabled_build_features ) def _get_test_discovery_zip_regexes(self) -> set[str]: build_target_regexes = set() for test_info in self.build_context.test_infos: tf_command = self._build_tf_command(test_info) discovery_agent = test_discovery_agent.TestDiscoveryAgent(tradefed_args=tf_command) for regex in discovery_agent.discover_test_zip_regexes(): build_target_regexes.add(regex) return build_target_regexes def _build_tf_command(self, test_info) -> list[str]: command = [test_info.command] for extra_option in test_info.extra_options: if not extra_option.get('key'): continue arg_key = '--' + extra_option.get('key') if arg_key == '--build-id': command.append(arg_key) command.append(os.environ.get('BUILD_NUMBER')) continue if extra_option.get('values'): for value in extra_option.get('values'): command.append(arg_key) command.append(value) else: command.append(arg_key) return command @dataclass(frozen=True) class BuildPlan: build_targets: set[str] packaging_commands_getters: list[Callable[[], list[list[str]]]] def build_test_suites(argv: list[str]) -> int: """Builds all test suites passed in, optimizing based on the build_context content. Args: argv: The command line arguments passed in. Returns: The exit code of the build. """ get_metrics_agent().analysis_start() try: args = parse_args(argv) check_required_env() build_context = BuildContext(load_build_context()) build_planner = BuildPlanner( build_context, args, optimized_targets.OPTIMIZED_BUILD_TARGETS ) build_plan = build_planner.create_build_plan() except: raise finally: get_metrics_agent().analysis_end() try: execute_build_plan(build_plan) except BuildFailureError as e: logging.error('Build command failed! Check build_log for details.') return e.return_code finally: get_metrics_agent().end_reporting() return 0 def parse_args(argv: list[str]) -> argparse.Namespace: argparser = argparse.ArgumentParser() argparser.add_argument( 'extra_targets', nargs='*', help='Extra test suites to build.' ) argparser.add_argument( '--device-build', action='store_true', help='Flag to indicate running a device build.', ) argparser.add_argument( '--test_discovery_info_mode', action='store_true', help='Flag to enable running test discovery in info only mode.', ) return argparser.parse_args(argv) def check_required_env(): """Check for required env vars. Raises: RuntimeError: If any required env vars are not found. """ missing_env_vars = sorted(v for v in REQUIRED_ENV_VARS if v not in os.environ) if not missing_env_vars: return t = ','.join(missing_env_vars) raise Error(f'Missing required environment variables: {t}') def load_build_context(): build_context_path = pathlib.Path(os.environ.get('BUILD_CONTEXT', '')) if build_context_path.is_file(): try: with open(build_context_path, 'r') as f: return json.load(f) except json.decoder.JSONDecodeError as e: raise Error(f'Failed to load JSON file: {build_context_path}') logging.info('No BUILD_CONTEXT found, skipping optimizations.') return empty_build_context() def empty_build_context(): return {'enabledBuildFeatures': []} def execute_build_plan(build_plan: BuildPlan): build_command = [] build_command.append(get_top().joinpath(SOONG_UI_EXE_REL_PATH)) build_command.append('--make-mode') build_command.extend(build_plan.build_targets) logging.info(f'Running build command: {build_command}') try: run_command(build_command) except subprocess.CalledProcessError as e: raise BuildFailureError(e.returncode) from e get_metrics_agent().packaging_start() try: for packaging_commands_getter in build_plan.packaging_commands_getters: for packaging_command in packaging_commands_getter(): run_command(packaging_command) except subprocess.CalledProcessError as e: raise BuildFailureError(e.returncode) from e finally: get_metrics_agent().packaging_end() def get_top() -> pathlib.Path: return pathlib.Path(os.environ['TOP']) def run_command(args: list[str], stdout=None): subprocess.run(args=args, check=True, stdout=stdout) def get_metrics_agent(): return metrics_agent.MetricsAgent.instance() def main(argv): dist_dir = os.environ.get('DIST_DIR') if dist_dir: log_file = pathlib.Path(dist_dir) / LOG_PATH logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename=log_file, ) sys.exit(build_test_suites(argv)) if __name__ == '__main__': main(sys.argv[1:]) ================================================ FILE: ci/build_test_suites_local_test.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for build_test_suites that require a local build env.""" import os import pathlib import shutil import signal import subprocess import tempfile import time import ci_test_lib class BuildTestSuitesLocalTest(ci_test_lib.TestCase): def setUp(self): self.top_dir = pathlib.Path(os.environ['ANDROID_BUILD_TOP']).resolve() self.executable = self.top_dir.joinpath('build/make/ci/build_test_suites') self.process_session = ci_test_lib.TemporaryProcessSession(self) self.temp_dir = ci_test_lib.TestTemporaryDirectory.create(self) def build_subprocess_args(self, build_args: list[str]): env = os.environ.copy() env['TOP'] = str(self.top_dir) env['OUT_DIR'] = self.temp_dir args = ([self.executable] + build_args,) kwargs = { 'cwd': self.top_dir, 'env': env, 'text': True, } return (args, kwargs) def run_build(self, build_args: list[str]) -> subprocess.CompletedProcess: args, kwargs = self.build_subprocess_args(build_args) return subprocess.run( *args, **kwargs, check=True, capture_output=True, timeout=5 * 60, ) def assert_children_alive(self, children: list[int]): for c in children: self.assertTrue(ci_test_lib.process_alive(c)) def assert_children_dead(self, children: list[int]): for c in children: self.assertFalse(ci_test_lib.process_alive(c)) def test_fails_for_invalid_arg(self): invalid_arg = '--invalid-arg' with self.assertRaises(subprocess.CalledProcessError) as cm: self.run_build([invalid_arg]) self.assertIn(invalid_arg, cm.exception.stderr) def test_builds_successfully(self): self.run_build(['nothing']) def test_can_interrupt_build(self): args, kwargs = self.build_subprocess_args(['general-tests']) p = self.process_session.create(args, kwargs) # TODO(lucafarsi): Replace this (and other instances) with a condition. time.sleep(5) # Wait for the build to get going. self.assertIsNone(p.poll()) # Check that the process is still alive. children = query_child_pids(p.pid) self.assert_children_alive(children) p.send_signal(signal.SIGINT) p.wait() time.sleep(5) # Wait for things to die out. self.assert_children_dead(children) def test_can_kill_build_process_group(self): args, kwargs = self.build_subprocess_args(['general-tests']) p = self.process_session.create(args, kwargs) time.sleep(5) # Wait for the build to get going. self.assertIsNone(p.poll()) # Check that the process is still alive. children = query_child_pids(p.pid) self.assert_children_alive(children) os.killpg(os.getpgid(p.pid), signal.SIGKILL) p.wait() time.sleep(5) # Wait for things to die out. self.assert_children_dead(children) # TODO(hzalek): Replace this with `psutils` once available in the tree. def query_child_pids(parent_pid: int) -> set[int]: p = subprocess.run( ['pgrep', '-P', str(parent_pid)], check=True, capture_output=True, text=True, ) return {int(pid) for pid in p.stdout.splitlines()} if __name__ == '__main__': ci_test_lib.main() ================================================ FILE: ci/build_test_suites_test.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for build_test_suites.py""" import argparse import functools from importlib import resources import json import multiprocessing import os import pathlib import shutil import signal import stat import subprocess import sys import tempfile import textwrap import time from typing import Callable import unittest from unittest import mock from build_context import BuildContext import build_test_suites import ci_test_lib import optimized_targets from pyfakefs import fake_filesystem_unittest import metrics_agent import test_discovery_agent class BuildTestSuitesTest(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() os_environ_patcher = mock.patch.dict('os.environ', {}) self.addCleanup(os_environ_patcher.stop) self.mock_os_environ = os_environ_patcher.start() subprocess_run_patcher = mock.patch('subprocess.run') self.addCleanup(subprocess_run_patcher.stop) self.mock_subprocess_run = subprocess_run_patcher.start() metrics_agent_finalize_patcher = mock.patch('metrics_agent.MetricsAgent.end_reporting') self.addCleanup(metrics_agent_finalize_patcher.stop) self.mock_metrics_agent_end = metrics_agent_finalize_patcher.start() self._setup_working_build_env() def test_missing_target_release_env_var_raises(self): del os.environ['TARGET_RELEASE'] with self.assert_raises_word(build_test_suites.Error, 'TARGET_RELEASE'): build_test_suites.main([]) def test_missing_target_product_env_var_raises(self): del os.environ['TARGET_PRODUCT'] with self.assert_raises_word(build_test_suites.Error, 'TARGET_PRODUCT'): build_test_suites.main([]) def test_missing_top_env_var_raises(self): del os.environ['TOP'] with self.assert_raises_word(build_test_suites.Error, 'TOP'): build_test_suites.main([]) def test_missing_dist_dir_env_var_raises(self): del os.environ['DIST_DIR'] with self.assert_raises_word(build_test_suites.Error, 'DIST_DIR'): build_test_suites.main([]) def test_invalid_arg_raises(self): invalid_args = ['--invalid_arg'] with self.assertRaisesRegex(SystemExit, '2'): build_test_suites.main(invalid_args) def test_build_failure_returns(self): self.mock_subprocess_run.side_effect = subprocess.CalledProcessError( 42, None ) with self.assertRaisesRegex(SystemExit, '42'): build_test_suites.main([]) def test_incorrectly_formatted_build_context_raises(self): build_context = self.fake_top.joinpath('build_context') build_context.touch() os.environ['BUILD_CONTEXT'] = str(build_context) with self.assert_raises_word(build_test_suites.Error, 'JSON'): build_test_suites.main([]) def test_build_success_returns(self): with self.assertRaisesRegex(SystemExit, '0'): build_test_suites.main([]) def assert_raises_word(self, cls, word): return self.assertRaisesRegex(cls, rf'\b{word}\b') def _setup_working_build_env(self): self.fake_top = pathlib.Path('/fake/top') self.fake_top.mkdir(parents=True) self.soong_ui_dir = self.fake_top.joinpath('build/soong') self.soong_ui_dir.mkdir(parents=True, exist_ok=True) self.logs_dir = self.fake_top.joinpath('dist/logs') self.logs_dir.mkdir(parents=True, exist_ok=True) self.soong_ui = self.soong_ui_dir.joinpath('soong_ui.bash') self.soong_ui.touch() self.mock_os_environ.update({ 'TARGET_RELEASE': 'release', 'TARGET_PRODUCT': 'product', 'TOP': str(self.fake_top), 'DIST_DIR': str(self.fake_top.joinpath('dist')), }) self.mock_subprocess_run.return_value = 0 class RunCommandIntegrationTest(ci_test_lib.TestCase): def setUp(self): self.temp_dir = ci_test_lib.TestTemporaryDirectory.create(self) # Copy the Python executable from 'non-code' resources and make it # executable for use by tests that launch a subprocess. Note that we don't # use Python's native `sys.executable` property since that is not set when # running via the embedded launcher. base_name = 'py3-cmd' dest_file = self.temp_dir.joinpath(base_name) with resources.as_file( resources.files('testdata').joinpath(base_name) ) as p: shutil.copy(p, dest_file) dest_file.chmod(dest_file.stat().st_mode | stat.S_IEXEC) self.python_executable = dest_file self._managed_processes = [] def tearDown(self): self._terminate_managed_processes() def test_raises_on_nonzero_exit(self): with self.assertRaises(Exception): build_test_suites.run_command([ self.python_executable, '-c', textwrap.dedent(f"""\ import sys sys.exit(1) """), ]) def test_streams_stdout(self): def run_slow_command(stdout_file, marker): with open(stdout_file, 'w') as f: build_test_suites.run_command( [ self.python_executable, '-c', textwrap.dedent(f"""\ import time print('{marker}', end='', flush=True) # Keep process alive until we check stdout. time.sleep(10) """), ], stdout=f, ) marker = 'Spinach' stdout_file = self.temp_dir.joinpath('stdout.txt') p = self.start_process(target=run_slow_command, args=[stdout_file, marker]) self.assert_file_eventually_contains(stdout_file, marker) def test_propagates_interruptions(self): def run(pid_file): build_test_suites.run_command([ self.python_executable, '-c', textwrap.dedent(f"""\ import os import pathlib import time pathlib.Path('{pid_file}').write_text(str(os.getpid())) # Keep the process alive for us to explicitly interrupt it. time.sleep(10) """), ]) pid_file = self.temp_dir.joinpath('pid.txt') p = self.start_process(target=run, args=[pid_file]) subprocess_pid = int(read_eventual_file_contents(pid_file)) os.kill(p.pid, signal.SIGINT) p.join() self.assert_process_eventually_dies(p.pid) self.assert_process_eventually_dies(subprocess_pid) def start_process(self, *args, **kwargs) -> multiprocessing.Process: p = multiprocessing.Process(*args, **kwargs) self._managed_processes.append(p) p.start() return p def assert_process_eventually_dies(self, pid: int): try: wait_until(lambda: not ci_test_lib.process_alive(pid)) except TimeoutError as e: self.fail(f'Process {pid} did not die after a while: {e}') def assert_file_eventually_contains(self, file: pathlib.Path, substring: str): wait_until(lambda: file.is_file() and file.stat().st_size > 0) self.assertIn(substring, read_file_contents(file)) def _terminate_managed_processes(self): for p in self._managed_processes: if not p.is_alive(): continue # We terminate the process with `SIGINT` since using `terminate` or # `SIGKILL` doesn't kill any grandchild processes and we don't have # `psutil` available to easily query all children. os.kill(p.pid, signal.SIGINT) class BuildPlannerTest(unittest.TestCase): class TestOptimizedBuildTarget(optimized_targets.OptimizedBuildTarget): def __init__( self, target, build_context, args, output_targets, packaging_commands ): super().__init__(target, build_context, args) self.output_targets = output_targets self.packaging_commands = packaging_commands def get_build_targets_impl(self): return self.output_targets def get_package_outputs_commands_impl(self): return self.packaging_commands def get_enabled_flag(self): return f'{self.target}_enabled' def setUp(self): test_discovery_agent_patcher = mock.patch('test_discovery_agent.TestDiscoveryAgent.discover_test_zip_regexes') self.addCleanup(test_discovery_agent_patcher.stop) self.mock_test_discovery_agent_end = test_discovery_agent_patcher.start() def test_build_optimization_off_builds_everything(self): build_targets = {'target_1', 'target_2'} build_planner = self.create_build_planner( build_context=self.create_build_context(optimized_build_enabled=False), build_targets=build_targets, ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_targets, build_plan.build_targets) def test_build_optimization_off_doesnt_package(self): build_targets = {'target_1', 'target_2'} build_planner = self.create_build_planner( build_context=self.create_build_context(optimized_build_enabled=False), build_targets=build_targets, ) build_plan = build_planner.create_build_plan() for packaging_command in self.run_packaging_commands(build_plan): self.assertEqual(len(packaging_command), 0) def test_build_optimization_on_optimizes_target(self): build_targets = {'target_1', 'target_2'} build_planner = self.create_build_planner( build_targets=build_targets, build_context=self.create_build_context( enabled_build_features=[{'name': self.get_target_flag('target_1')}], test_context=self.get_test_context('target_1'), ), ) build_plan = build_planner.create_build_plan() expected_targets = {self.get_optimized_target_name('target_1'), 'target_2'} self.assertSetEqual(expected_targets, build_plan.build_targets) def test_build_optimization_on_packages_target(self): build_targets = {'target_1', 'target_2'} optimized_target_name = self.get_optimized_target_name('target_1') packaging_commands = [[f'packaging {optimized_target_name}']] build_planner = self.create_build_planner( build_targets=build_targets, build_context=self.create_build_context( enabled_build_features=[{'name': self.get_target_flag('target_1')}], test_context=self.get_test_context('target_1'), ), packaging_commands=packaging_commands, ) build_plan = build_planner.create_build_plan() self.assertIn(packaging_commands, self.run_packaging_commands(build_plan)) def test_individual_build_optimization_off_doesnt_optimize(self): build_targets = {'target_1', 'target_2'} build_planner = self.create_build_planner( build_targets=build_targets, ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_targets, build_plan.build_targets) def test_individual_build_optimization_off_doesnt_package(self): build_targets = {'target_1', 'target_2'} packaging_commands = [['packaging command']] build_planner = self.create_build_planner( build_targets=build_targets, packaging_commands=packaging_commands, ) build_plan = build_planner.create_build_plan() for packaging_command in self.run_packaging_commands(build_plan): self.assertEqual(len(packaging_command), 0) def test_target_output_used_target_built(self): build_target = 'test_target' build_planner = self.create_build_planner( build_targets={build_target}, build_context=self.create_build_context( test_context=self.get_test_context(build_target), enabled_build_features=[{'name': 'test_target_unused_exclusion'}], ), ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_plan.build_targets, {build_target}) def test_target_regex_used_target_built(self): build_target = 'test_target' test_context = self.get_test_context(build_target) test_context['testInfos'][0]['extraOptions'] = [{ 'key': 'additional-files-filter', 'values': [f'.*{build_target}.*\.zip'], }] build_planner = self.create_build_planner( build_targets={build_target}, build_context=self.create_build_context( test_context=test_context, enabled_build_features=[{'name': 'test_target_unused_exclusion'}], ), ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_plan.build_targets, {build_target}) def test_target_output_not_used_target_not_built(self): build_target = 'test_target' test_context = self.get_test_context(build_target) test_context['testInfos'][0]['extraOptions'] = [] build_planner = self.create_build_planner( build_targets={build_target}, build_context=self.create_build_context( test_context=test_context, enabled_build_features=[{'name': 'test_target_unused_exclusion'}], ), ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_plan.build_targets, set()) def test_target_regex_matching_not_too_broad(self): build_target = 'test_target' test_context = self.get_test_context(build_target) test_context['testInfos'][0]['extraOptions'] = [{ 'key': 'additional-files-filter', 'values': [f'.*a{build_target}.*\.zip'], }] build_planner = self.create_build_planner( build_targets={build_target}, build_context=self.create_build_context( test_context=test_context, enabled_build_features=[{'name': 'test_target_unused_exclusion'}], ), ) build_plan = build_planner.create_build_plan() self.assertSetEqual(build_plan.build_targets, set()) def create_build_planner( self, build_targets: set[str], build_context: BuildContext = None, args: argparse.Namespace = None, target_optimizations: dict[ str, optimized_targets.OptimizedBuildTarget ] = None, packaging_commands: list[list[str]] = [], ) -> build_test_suites.BuildPlanner: if not build_context: build_context = self.create_build_context() if not args: args = self.create_args(extra_build_targets=build_targets) if not target_optimizations: target_optimizations = self.create_target_optimizations( build_context, build_targets, packaging_commands, ) return build_test_suites.BuildPlanner( build_context, args, target_optimizations ) def create_build_context( self, optimized_build_enabled: bool = True, enabled_build_features: list[dict[str, str]] = [], test_context: dict[str, any] = {}, ) -> BuildContext: build_context_dict = {} build_context_dict['enabledBuildFeatures'] = enabled_build_features if optimized_build_enabled: build_context_dict['enabledBuildFeatures'].append( {'name': 'optimized_build'} ) build_context_dict['testContext'] = test_context return BuildContext(build_context_dict) def create_args( self, extra_build_targets: set[str] = set() ) -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument('extra_targets', nargs='*') return parser.parse_args(extra_build_targets) def create_target_optimizations( self, build_context: BuildContext, build_targets: set[str], packaging_commands: list[list[str]] = [], ): target_optimizations = dict() for target in build_targets: target_optimizations[target] = functools.partial( self.TestOptimizedBuildTarget, output_targets={self.get_optimized_target_name(target)}, packaging_commands=packaging_commands, ) return target_optimizations def get_target_flag(self, target: str): return f'{target}_enabled' def get_optimized_target_name(self, target: str): return f'{target}_optimized' def get_test_context(self, target: str): return { 'testInfos': [ { 'name': 'atp_test', 'target': 'test_target', 'branch': 'branch', 'extraOptions': [{ 'key': 'additional-files-filter', 'values': [f'{target}.zip'], }], 'command': '/tf/command', 'extraBuildTargets': [ 'extra_build_target', ], }, ], } def run_packaging_commands(self, build_plan: build_test_suites.BuildPlan): return [ packaging_command_getter() for packaging_command_getter in build_plan.packaging_commands_getters ] def wait_until( condition_function: Callable[[], bool], timeout_secs: float = 3.0, polling_interval_secs: float = 0.1, ): """Waits until a condition function returns True.""" start_time_secs = time.time() while not condition_function(): if time.time() - start_time_secs > timeout_secs: raise TimeoutError( f'Condition not met within timeout: {timeout_secs} seconds' ) time.sleep(polling_interval_secs) def read_file_contents(file: pathlib.Path) -> str: with open(file, 'r') as f: return f.read() def read_eventual_file_contents(file: pathlib.Path) -> str: wait_until(lambda: file.is_file() and file.stat().st_size > 0) return read_file_contents(file) if __name__ == '__main__': ci_test_lib.main() ================================================ FILE: ci/buildbot.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Utilities for interacting with buildbot, with a simulation in a local environment""" import os import sys # Check that the script is running from the root of the tree. Prevents subtle # errors later, and CI always runs from the root of the tree. if not os.path.exists("build/make/ci/buildbot.py"): raise Exception("CI script must be run from the root of the tree instead of: " + os.getcwd()) # Check that we are using the hermetic interpreter if "prebuilts/build-tools/" not in sys.executable: raise Exception("CI script must be run using the hermetic interpreter from " + "prebuilts/build-tools instead of: " + sys.executable) def OutDir(): "Get the out directory. Will create it if needed." result = os.environ.get("OUT_DIR", "out") os.makedirs(result, exist_ok=True) return result def DistDir(): "Get the dist directory. Will create it if needed." result = os.environ.get("DIST_DIR", os.path.join(OutDir(), "dist")) os.makedirs(result, exist_ok=True) return result ================================================ FILE: ci/ci_test_lib.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Testing utilities for tests in the CI package.""" import logging import os import unittest import subprocess import pathlib import shutil import tempfile # Export the TestCase class to reduce the number of imports tests have to list. TestCase = unittest.TestCase def process_alive(pid): """Check For the existence of a pid.""" try: os.kill(pid, 0) except OSError: return False return True class TemporaryProcessSession: def __init__(self, test_case: TestCase): self._created_processes = [] test_case.addCleanup(self.cleanup) def create(self, args, kwargs): p = subprocess.Popen(*args, **kwargs, start_new_session=True) self._created_processes.append(p) return p def cleanup(self): for p in self._created_processes: if not process_alive(p.pid): return os.killpg(os.getpgid(p.pid), signal.SIGKILL) class TestTemporaryDirectory: def __init__(self, delete: bool, ): self._delete = delete @classmethod def create(cls, test_case: TestCase, delete: bool = True): temp_dir = TestTemporaryDirectory(delete) temp_dir._dir = pathlib.Path(tempfile.mkdtemp()) test_case.addCleanup(temp_dir.cleanup) return temp_dir._dir def get_dir(self): return self._dir def cleanup(self): if not self._delete: return shutil.rmtree(self._dir, ignore_errors=True) def main(): # Disable logging since it breaks the TF Python test output parser. # TODO(hzalek): Use TF's `test-output-file` option to re-enable logging. logging.getLogger().disabled = True unittest.main() ================================================ FILE: ci/dump_product_config ================================================ #!prebuilts/build-tools/linux-x86/bin/py3-cmd -B # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Script to collect all of the make variables from all product config combos. This script must be run from the root of the source tree. See GetArgs() below or run dump_product_config for more information. """ import argparse import asyncio import contextlib import csv import dataclasses import json import multiprocessing import os import subprocess import sys import time from typing import List, Dict, Tuple, Optional import buildbot # We have some BIG variables csv.field_size_limit(sys.maxsize) class DataclassJSONEncoder(json.JSONEncoder): """JSONEncoder for our custom types.""" def default(self, o): if dataclasses.is_dataclass(o): return dataclasses.asdict(o) return super().default(o) def GetProducts(): """Get the all of the available TARGET_PRODUCT values.""" try: stdout = subprocess.check_output(["build/soong/bin/list_products"], text=True) except subprocess.CalledProcessError: sys.exit(1) return [s.strip() for s in stdout.splitlines() if s.strip()] def GetReleases(product): """For a given product, get the release configs available to it.""" if True: # Hard code the list mainline_products = [ "module_arm", "module_x86", "module_arm64", "module_riscv64", "module_x86_64", "module_arm64only", "module_x86_64only", ] if product in mainline_products: return ["trunk_staging", "trunk", "mainline"] else: return ["trunk_staging", "trunk", "next"] else: # Get it from the build system try: stdout = subprocess.check_output(["build/soong/bin/list_releases", product], text=True) except subprocess.CalledProcessError: sys.exit(1) return [s.strip() for s in stdout.splitlines() if s.strip()] def GenerateAllLunchTargets(): """Generate the full list of lunch targets.""" for product in GetProducts(): for release in GetReleases(product): for variant in ["user", "userdebug", "eng"]: yield (product, release, variant) async def ParallelExec(parallelism, tasks): ''' ParallelExec takes a parallelism number, and an iterator of tasks to run. Then it will run all the tasks, but a maximum of parallelism will be run at any given time. The tasks must be async functions that accept one argument, which will be an integer id of the worker that they're running on. ''' tasks = iter(tasks) overall_start = time.monotonic() # lists so they can be modified from the inner function total_duration = [0] count = [0] async def dispatch(worker): while True: try: task = next(tasks) item_start = time.monotonic() await task(worker) now = time.monotonic() item_duration = now - item_start count[0] += 1 total_duration[0] += item_duration sys.stderr.write(f"Timing: Items processed: {count[0]}, Wall time: {now-overall_start:0.1f} sec, Throughput: {(now-overall_start)/count[0]:0.3f} sec per item, Average duration: {total_duration[0]/count[0]:0.1f} sec\n") except StopIteration: return await asyncio.gather(*[dispatch(worker) for worker in range(parallelism)]) async def DumpProductConfigs(out, generator, out_dir): """Collects all of the product config data and store it in file.""" # Write the outer json list by hand so we can stream it out.write("[") try: first_result = [True] # a list so it can be modified from the inner function def run(lunch): async def curried(worker): sys.stderr.write(f"running: {'-'.join(lunch)}\n") result = await DumpOneProductConfig(lunch, os.path.join(out_dir, f"lunchable_{worker}")) if first_result[0]: out.write("\n") first_result[0] = False else: out.write(",\n") result.dumpToFile(out) sys.stderr.write(f"finished: {'-'.join(lunch)}\n") return curried await ParallelExec(multiprocessing.cpu_count(), (run(lunch) for lunch in generator)) finally: # Close the json regardless of how we exit out.write("\n]\n") @dataclasses.dataclass(frozen=True) class Variable: """A variable name, value and where it was set.""" name: str value: str location: str @dataclasses.dataclass(frozen=True) class ProductResult: product: str release: str variant: str board_includes: List[str] product_includes: Dict[str, List[str]] product_graph: List[Tuple[str, str]] board_vars: List[Variable] product_vars: List[Variable] def dumpToFile(self, f): json.dump(self, f, sort_keys=True, indent=2, cls=DataclassJSONEncoder) @dataclasses.dataclass(frozen=True) class ProductError: product: str release: str variant: str error: str def dumpToFile(self, f): json.dump(self, f, sort_keys=True, indent=2, cls=DataclassJSONEncoder) def NormalizeInheritGraph(lists): """Flatten the inheritance graph to a simple list for easier querying.""" result = set() for item in lists: for i in range(len(item)): result.add((item[i+1] if i < len(item)-1 else "", item[i])) return sorted(list(result)) def ParseDump(lunch, filename) -> ProductResult: """Parses the csv and returns a tuple of the data.""" def diff(initial, final): return [after for after in final.values() if initial.get(after.name, Variable(after.name, "", "")).value != after.value] product_initial = {} product_final = {} board_initial = {} board_final = {} inherit_product = [] # The stack of inherit-product calls product_includes = {} # Other files included by each of the properly imported files board_includes = [] # Files included by boardconfig with open(filename) as f: phase = "" for line in csv.reader(f): if line[0] == "phase": phase = line[1] elif line[0] == "val": # TOOD: We should skip these somewhere else. if line[3].startswith("_ALL_RELEASE_FLAGS"): continue if line[3].startswith("PRODUCTS."): continue if phase == "PRODUCTS": if line[2] == "initial": product_initial[line[3]] = Variable(line[3], line[4], line[5]) if phase == "PRODUCT-EXPAND": if line[2] == "final": product_final[line[3]] = Variable(line[3], line[4], line[5]) if phase == "BOARD": if line[2] == "initial": board_initial[line[3]] = Variable(line[3], line[4], line[5]) if line[2] == "final": board_final[line[3]] = Variable(line[3], line[4], line[5]) elif line[0] == "imported": imports = [s.strip() for s in line[1].split()] if imports: inherit_product.append(imports) inc = [s.strip() for s in line[2].split()] for f in inc: product_includes.setdefault(imports[0], []).append(f) elif line[0] == "board_config_files": board_includes += [s.strip() for s in line[1].split()] return ProductResult( product = lunch[0], release = lunch[1], variant = lunch[2], product_vars = diff(product_initial, product_final), board_vars = diff(board_initial, board_final), product_graph = NormalizeInheritGraph(inherit_product), product_includes = product_includes, board_includes = board_includes ) async def DumpOneProductConfig(lunch, out_dir) -> ProductResult | ProductError: """Print a single config's lunch info to stdout.""" product, release, variant = lunch dumpconfig_file = os.path.join(out_dir, f"{product}-{release}-{variant}.csv") # Run get_build_var to bootstrap soong_ui for this target env = dict(os.environ) env["TARGET_PRODUCT"] = product env["TARGET_RELEASE"] = release env["TARGET_BUILD_VARIANT"] = variant env["OUT_DIR"] = out_dir process = await asyncio.create_subprocess_exec( "build/soong/bin/get_build_var", "TARGET_PRODUCT", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env ) stdout, _ = await process.communicate() stdout = stdout.decode() if process.returncode != 0: return ProductError( product = product, release = release, variant = variant, error = stdout ) else: # Run kati to extract the data process = await asyncio.create_subprocess_exec( "prebuilts/build-tools/linux-x86/bin/ckati", "-f", "build/make/core/dumpconfig.mk", f"TARGET_PRODUCT={product}", f"TARGET_RELEASE={release}", f"TARGET_BUILD_VARIANT={variant}", f"DUMPCONFIG_FILE={dumpconfig_file}", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env ) stdout, _ = await process.communicate() if process.returncode != 0: stdout = stdout.decode() return ProductError( product = product, release = release, variant = variant, error = stdout ) else: # Parse and record the output return ParseDump(lunch, dumpconfig_file) def GetArgs(): """Parse command line arguments.""" parser = argparse.ArgumentParser( description="Collect all of the make variables from product config.", epilog="NOTE: This script must be run from the root of the source tree.") parser.add_argument("--lunch", nargs="*") parser.add_argument("--dist", action="store_true") return parser.parse_args() async def main(): args = GetArgs() out_dir = buildbot.OutDir() if args.dist: cm = open(os.path.join(buildbot.DistDir(), "all_product_config.json"), "w") else: cm = contextlib.nullcontext(sys.stdout) with cm as out: if args.lunch: lunches = [lunch.split("-") for lunch in args.lunch] fail = False for i in range(len(lunches)): if len(lunches[i]) != 3: sys.stderr.write(f"Malformed lunch targets: {args.lunch[i]}\n") fail = True if fail: sys.exit(1) if len(lunches) == 1: result = await DumpOneProductConfig(lunches[0], out_dir) result.dumpToFile(out) out.write("\n") else: await DumpProductConfigs(out, lunches, out_dir) else: # All configs mode. This will exec single config mode in parallel # for each lunch combo. Write output to $DIST_DIR. await DumpProductConfigs(out, GenerateAllLunchTargets(), out_dir) if __name__ == "__main__": asyncio.run(main()) # vim: set syntax=python ts=4 sw=4 sts=4: ================================================ FILE: ci/metrics_agent.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """MetricsAgent is a singleton class that collects metrics for optimized build.""" from enum import Enum import time import metrics_pb2 import os import logging class MetricsAgent: _SOONG_METRICS_PATH = 'logs/soong_metrics' _DIST_DIR = 'DIST_DIR' _instance = None def __init__(self): raise RuntimeError( 'MetricsAgent cannot be instantialized, use instance() instead' ) @classmethod def instance(cls): if not cls._instance: cls._instance = cls.__new__(cls) cls._instance._proto = metrics_pb2.OptimizedBuildMetrics() cls._instance._init_proto() cls._instance._target_results = dict() return cls._instance def _init_proto(self): self._proto.analysis_perf.name = 'Optimized build analysis time.' self._proto.packaging_perf.name = 'Optimized build total packaging time.' def analysis_start(self): self._proto.analysis_perf.start_time = time.time_ns() def analysis_end(self): self._proto.analysis_perf.real_time = ( time.time_ns() - self._proto.analysis_perf.start_time ) def packaging_start(self): self._proto.packaging_perf.start_time = time.time_ns() def packaging_end(self): self._proto.packaging_perf.real_time = ( time.time_ns() - self._proto.packaging_perf.start_time ) def report_optimized_target(self, name: str): target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult() target_result.name = name target_result.optimized = True self._target_results[name] = target_result def report_unoptimized_target(self, name: str, optimization_rationale: str): target_result = metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult() target_result.name = name target_result.optimization_rationale = optimization_rationale target_result.optimized = False self._target_results[name] = target_result def target_packaging_start(self, name: str): target_result = self._target_results.get(name) target_result.packaging_perf.start_time = time.time_ns() self._target_results[name] = target_result def target_packaging_end(self, name: str): target_result = self._target_results.get(name) target_result.packaging_perf.real_time = ( time.time_ns() - target_result.packaging_perf.start_time ) def add_target_artifact( self, target_name: str, artifact_name: str, size: int, included_modules: set[str], ): target_result = self.target_results.get(target_name) artifact = ( metrics_pb2.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact() ) artifact.name = artifact_name artifact.size = size for module in included_modules: artifact.included_modules.add(module) target_result.output_artifacts.add(artifact) def end_reporting(self): for target_result in self._target_results.values(): self._proto.target_result.append(target_result) soong_metrics_proto = metrics_pb2.MetricsBase() # Read in existing metrics that should have been written out by the soong # build command so that we don't overwrite them. with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'rb') as f: soong_metrics_proto.ParseFromString(f.read()) soong_metrics_proto.optimized_build_metrics.CopyFrom(self._proto) logging.info(soong_metrics_proto) with open(os.path.join(os.environ[self._DIST_DIR], self._SOONG_METRICS_PATH), 'wb') as f: f.write(soong_metrics_proto.SerializeToString()) ================================================ FILE: ci/optimized_targets.py ================================================ # # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from abc import ABC import argparse import functools import json import logging import os import pathlib import subprocess from build_context import BuildContext import test_mapping_module_retriever class OptimizedBuildTarget(ABC): """A representation of an optimized build target. This class will determine what targets to build given a given build_cotext and will have a packaging function to generate any necessary output zips for the build. """ _SOONG_UI_BASH_PATH = 'build/soong/soong_ui.bash' _PREBUILT_SOONG_ZIP_PATH = 'prebuilts/build-tools/linux-x86/bin/soong_zip' def __init__( self, target: str, build_context: BuildContext, args: argparse.Namespace, ): self.target = target self.build_context = build_context self.args = args def get_build_targets(self) -> set[str]: features = self.build_context.enabled_build_features if self.get_enabled_flag() in features: self.modules_to_build = self.get_build_targets_impl() return self.modules_to_build self.modules_to_build = {self.target} return {self.target} def get_package_outputs_commands(self) -> list[list[str]]: features = self.build_context.enabled_build_features if self.get_enabled_flag() in features: return self.get_package_outputs_commands_impl() return [] def get_package_outputs_commands_impl(self) -> list[list[str]]: raise NotImplementedError( 'get_package_outputs_commands_impl not implemented in' f' {type(self).__name__}' ) def get_enabled_flag(self): raise NotImplementedError( f'get_enabled_flag not implemented in {type(self).__name__}' ) def get_build_targets_impl(self) -> set[str]: raise NotImplementedError( f'get_build_targets_impl not implemented in {type(self).__name__}' ) def _generate_zip_options_for_items( self, prefix: str = '', relative_root: str = '', list_files: list[str] | None = None, files: list[str] | None = None, directories: list[str] | None = None, ) -> list[str]: if not list_files and not files and not directories: raise RuntimeError( f'No items specified to be added to zip! Prefix: {prefix}, Relative' f' root: {relative_root}' ) command_segment = [] # These are all soong_zip options so consult soong_zip --help for specifics. if prefix: command_segment.append('-P') command_segment.append(prefix) if relative_root: command_segment.append('-C') command_segment.append(relative_root) if list_files: for list_file in list_files: command_segment.append('-l') command_segment.append(list_file) if files: for file in files: command_segment.append('-f') command_segment.append(file) if directories: for directory in directories: command_segment.append('-D') command_segment.append(directory) return command_segment def _query_soong_vars( self, src_top: pathlib.Path, soong_vars: list[str] ) -> dict[str, str]: process_result = subprocess.run( args=[ f'{src_top / self._SOONG_UI_BASH_PATH}', '--dumpvars-mode', f'--abs-vars={" ".join(soong_vars)}', ], env=os.environ, check=False, capture_output=True, text=True, ) if not process_result.returncode == 0: logging.error('soong dumpvars command failed! stderr:') logging.error(process_result.stderr) raise RuntimeError('Soong dumpvars failed! See log for stderr.') if not process_result.stdout: raise RuntimeError( 'Necessary soong variables ' + soong_vars + ' not found.' ) try: return { line.split('=')[0]: line.split('=')[1].strip("'") for line in process_result.stdout.strip().split('\n') } except IndexError as e: raise RuntimeError( 'Error parsing soong dumpvars output! See output here:' f' {process_result.stdout}', e, ) def _base_zip_command( self, src_top: pathlib.Path, dist_dir: pathlib.Path, name: str ) -> list[str]: return [ f'{src_top / self._PREBUILT_SOONG_ZIP_PATH }', '-d', '-o', f'{dist_dir / name}', ] class NullOptimizer(OptimizedBuildTarget): """No-op target optimizer. This will simply build the same target it was given and do nothing for the packaging step. """ def __init__(self, target): self.target = target def get_build_targets(self): return {self.target} def get_package_outputs_commands(self): return [] class ChangeInfo: def __init__(self, change_info_file_path): try: with open(change_info_file_path) as change_info_file: change_info_contents = json.load(change_info_file) except json.decoder.JSONDecodeError: logging.error(f'Failed to load CHANGE_INFO: {change_info_file_path}') raise self._change_info_contents = change_info_contents def find_changed_files(self) -> set[str]: changed_files = set() for change in self._change_info_contents['changes']: project_path = change.get('projectPath') + '/' for revision in change.get('revisions'): for file_info in revision.get('fileInfos'): changed_files.add(project_path + file_info.get('path')) return changed_files class GeneralTestsOptimizer(OptimizedBuildTarget): """general-tests optimizer This optimizer reads in the list of changed files from the file located in env[CHANGE_INFO] and uses this list alongside the normal TEST MAPPING logic to determine what test mapping modules will run for the given changes. It then builds those modules and packages them in the same way general-tests.zip is normally built. """ # List of modules that are built alongside general-tests as dependencies. _REQUIRED_MODULES = frozenset([ 'cts-tradefed', 'vts-tradefed', 'compatibility-host-util', 'general-tests-shared-libs', ]) def get_build_targets_impl(self) -> set[str]: change_info_file_path = os.environ.get('CHANGE_INFO') if not change_info_file_path: logging.info( 'No CHANGE_INFO env var found, general-tests optimization disabled.' ) return {'general-tests'} test_infos = self.build_context.test_infos test_mapping_test_groups = set() for test_info in test_infos: is_test_mapping = test_info.is_test_mapping current_test_mapping_test_groups = test_info.test_mapping_test_groups uses_general_tests = test_info.build_target_used('general-tests') if uses_general_tests and not is_test_mapping: logging.info( 'Test uses general-tests.zip but is not test-mapping, general-tests' ' optimization disabled.' ) return {'general-tests'} if is_test_mapping: test_mapping_test_groups.update(current_test_mapping_test_groups) change_info = ChangeInfo(change_info_file_path) changed_files = change_info.find_changed_files() test_mappings = test_mapping_module_retriever.GetTestMappings( changed_files, set() ) modules_to_build = set(self._REQUIRED_MODULES) modules_to_build.update( test_mapping_module_retriever.FindAffectedModules( test_mappings, changed_files, test_mapping_test_groups ) ) return modules_to_build def get_package_outputs_commands_impl(self): src_top = pathlib.Path(os.environ.get('TOP', os.getcwd())) dist_dir = pathlib.Path(os.environ.get('DIST_DIR')) soong_vars = self._query_soong_vars( src_top, [ 'HOST_OUT_TESTCASES', 'TARGET_OUT_TESTCASES', 'PRODUCT_OUT', 'SOONG_HOST_OUT', 'HOST_OUT', ], ) host_out_testcases = pathlib.Path(soong_vars.get('HOST_OUT_TESTCASES')) target_out_testcases = pathlib.Path(soong_vars.get('TARGET_OUT_TESTCASES')) product_out = pathlib.Path(soong_vars.get('PRODUCT_OUT')) soong_host_out = pathlib.Path(soong_vars.get('SOONG_HOST_OUT')) host_out = pathlib.Path(soong_vars.get('HOST_OUT')) host_paths = [] target_paths = [] host_config_files = [] target_config_files = [] for module in self.modules_to_build: # The required modules are handled separately, no need to package. if module in self._REQUIRED_MODULES: continue host_path = host_out_testcases / module if os.path.exists(host_path): host_paths.append(host_path) self._collect_config_files(src_top, host_path, host_config_files) target_path = target_out_testcases / module if os.path.exists(target_path): target_paths.append(target_path) self._collect_config_files(src_top, target_path, target_config_files) if not os.path.exists(host_path) and not os.path.exists(target_path): logging.info(f'No host or target build outputs found for {module}.') zip_commands = [] zip_commands.extend( self._get_zip_test_configs_zips_commands( src_top, dist_dir, host_out, product_out, host_config_files, target_config_files, ) ) zip_command = self._base_zip_command(src_top, dist_dir, 'general-tests.zip') # Add host testcases. if host_paths: zip_command.extend( self._generate_zip_options_for_items( prefix='host', relative_root=f'{src_top / soong_host_out}', directories=host_paths, ) ) # Add target testcases. if target_paths: zip_command.extend( self._generate_zip_options_for_items( prefix='target', relative_root=f'{src_top / product_out}', directories=target_paths, ) ) # TODO(lucafarsi): Push this logic into a general-tests-minimal build command # Add necessary tools. These are also hardcoded in general-tests.mk. framework_path = soong_host_out / 'framework' zip_command.extend( self._generate_zip_options_for_items( prefix='host/tools', relative_root=str(framework_path), files=[ f"{framework_path / 'cts-tradefed.jar'}", f"{framework_path / 'compatibility-host-util.jar'}", f"{framework_path / 'vts-tradefed.jar'}", ], ) ) zip_commands.append(zip_command) return zip_commands def _collect_config_files( self, src_top: pathlib.Path, root_dir: pathlib.Path, config_files: list[str], ): for root, dirs, files in os.walk(src_top / root_dir): for file in files: if file.endswith('.config'): config_files.append(root_dir / file) def _get_zip_test_configs_zips_commands( self, src_top: pathlib.Path, dist_dir: pathlib.Path, host_out: pathlib.Path, product_out: pathlib.Path, host_config_files: list[str], target_config_files: list[str], ) -> tuple[list[str], list[str]]: """Generate general-tests_configs.zip and general-tests_list.zip. general-tests_configs.zip contains all of the .config files that were built and general-tests_list.zip contains a text file which lists all of the .config files that are in general-tests_configs.zip. general-tests_configs.zip is organized as follows: / host/ testcases/ test_1.config test_2.config ... target/ testcases/ test_1.config test_2.config ... So the process is we write out the paths to all the host config files into one file and all the paths to the target config files in another. We also write the paths to all the config files into a third file to use for general-tests_list.zip. Args: dist_dir: dist directory. host_out: host out directory. product_out: product out directory. host_config_files: list of all host config files. target_config_files: list of all target config files. Returns: The commands to generate general-tests_configs.zip and general-tests_list.zip """ with open( f"{host_out / 'host_general-tests_list'}", 'w' ) as host_list_file, open( f"{product_out / 'target_general-tests_list'}", 'w' ) as target_list_file, open( f"{host_out / 'general-tests_list'}", 'w' ) as list_file: for config_file in host_config_files: host_list_file.write(f'{config_file}' + '\n') list_file.write('host/' + os.path.relpath(config_file, host_out) + '\n') for config_file in target_config_files: target_list_file.write(f'{config_file}' + '\n') list_file.write( 'target/' + os.path.relpath(config_file, product_out) + '\n' ) zip_commands = [] tests_config_zip_command = self._base_zip_command( src_top, dist_dir, 'general-tests_configs.zip' ) tests_config_zip_command.extend( self._generate_zip_options_for_items( prefix='host', relative_root=str(host_out), list_files=[f"{host_out / 'host_general-tests_list'}"], ) ) tests_config_zip_command.extend( self._generate_zip_options_for_items( prefix='target', relative_root=str(product_out), list_files=[f"{product_out / 'target_general-tests_list'}"], ), ) zip_commands.append(tests_config_zip_command) tests_list_zip_command = self._base_zip_command( src_top, dist_dir, 'general-tests_list.zip' ) tests_list_zip_command.extend( self._generate_zip_options_for_items( relative_root=str(host_out), files=[f"{host_out / 'general-tests_list'}"], ) ) zip_commands.append(tests_list_zip_command) return zip_commands def get_enabled_flag(self): return 'general_tests_optimized' @classmethod def get_optimized_targets(cls) -> dict[str, OptimizedBuildTarget]: return {'general-tests': functools.partial(cls)} OPTIMIZED_BUILD_TARGETS = {} OPTIMIZED_BUILD_TARGETS.update(GeneralTestsOptimizer.get_optimized_targets()) ================================================ FILE: ci/optimized_targets_test.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for optimized_targets.py""" import json import logging import os import pathlib import re import subprocess import textwrap import unittest from unittest import mock from build_context import BuildContext import optimized_targets from pyfakefs import fake_filesystem_unittest class GeneralTestsOptimizerTest(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() os_environ_patcher = mock.patch.dict('os.environ', {}) self.addCleanup(os_environ_patcher.stop) self.mock_os_environ = os_environ_patcher.start() self._setup_working_build_env() self._write_change_info_file() test_mapping_dir = pathlib.Path('/project/path/file/path') test_mapping_dir.mkdir(parents=True) self._write_test_mapping_file() def _setup_working_build_env(self): self.change_info_file = pathlib.Path('/tmp/change_info') self._write_soong_ui_file() self._host_out_testcases = pathlib.Path('/tmp/top/host_out_testcases') self._host_out_testcases.mkdir(parents=True) self._target_out_testcases = pathlib.Path('/tmp/top/target_out_testcases') self._target_out_testcases.mkdir(parents=True) self._product_out = pathlib.Path('/tmp/top/product_out') self._product_out.mkdir(parents=True) self._soong_host_out = pathlib.Path('/tmp/top/soong_host_out') self._soong_host_out.mkdir(parents=True) self._host_out = pathlib.Path('/tmp/top/host_out') self._host_out.mkdir(parents=True) self._dist_dir = pathlib.Path('/tmp/top/out/dist') self._dist_dir.mkdir(parents=True) self.mock_os_environ.update({ 'CHANGE_INFO': str(self.change_info_file), 'TOP': '/tmp/top', 'DIST_DIR': '/tmp/top/out/dist', }) def _write_soong_ui_file(self): soong_path = pathlib.Path('/tmp/top/build/soong') soong_path.mkdir(parents=True) with open(os.path.join(soong_path, 'soong_ui.bash'), 'w') as f: f.write(""" #/bin/bash echo HOST_OUT_TESTCASES='/tmp/top/host_out_testcases' echo TARGET_OUT_TESTCASES='/tmp/top/target_out_testcases' echo PRODUCT_OUT='/tmp/top/product_out' echo SOONG_HOST_OUT='/tmp/top/soong_host_out' echo HOST_OUT='/tmp/top/host_out' """) os.chmod(os.path.join(soong_path, 'soong_ui.bash'), 0o666) def _write_change_info_file(self): change_info_contents = { 'changes': [{ 'projectPath': '/project/path', 'revisions': [{ 'fileInfos': [{ 'path': 'file/path/file_name', }], }], }] } with open(self.change_info_file, 'w') as f: json.dump(change_info_contents, f) def _write_test_mapping_file(self): test_mapping_contents = { 'test-mapping-group': [ { 'name': 'test_mapping_module', }, ], } with open('/project/path/file/path/TEST_MAPPING', 'w') as f: json.dump(test_mapping_contents, f) def test_general_tests_optimized(self): optimizer = self._create_general_tests_optimizer() build_targets = optimizer.get_build_targets() expected_build_targets = set( optimized_targets.GeneralTestsOptimizer._REQUIRED_MODULES ) expected_build_targets.add('test_mapping_module') self.assertSetEqual(build_targets, expected_build_targets) def test_no_change_info_no_optimization(self): del os.environ['CHANGE_INFO'] optimizer = self._create_general_tests_optimizer() build_targets = optimizer.get_build_targets() self.assertSetEqual(build_targets, {'general-tests'}) def test_mapping_groups_unused_module_not_built(self): test_context = self._create_test_context() test_context['testInfos'][0]['extraOptions'] = [ { 'key': 'additional-files-filter', 'values': ['general-tests.zip'], }, { 'key': 'test-mapping-test-group', 'values': ['unused-test-mapping-group'], }, ] optimizer = self._create_general_tests_optimizer( build_context=self._create_build_context(test_context=test_context) ) build_targets = optimizer.get_build_targets() expected_build_targets = set( optimized_targets.GeneralTestsOptimizer._REQUIRED_MODULES ) self.assertSetEqual(build_targets, expected_build_targets) def test_general_tests_used_by_non_test_mapping_test_no_optimization(self): test_context = self._create_test_context() test_context['testInfos'][0]['extraOptions'] = [{ 'key': 'additional-files-filter', 'values': ['general-tests.zip'], }] optimizer = self._create_general_tests_optimizer( build_context=self._create_build_context(test_context=test_context) ) build_targets = optimizer.get_build_targets() self.assertSetEqual(build_targets, {'general-tests'}) def test_malformed_change_info_raises(self): with open(self.change_info_file, 'w') as f: f.write('not change info') optimizer = self._create_general_tests_optimizer() with self.assertRaises(json.decoder.JSONDecodeError): build_targets = optimizer.get_build_targets() def test_malformed_test_mapping_raises(self): with open('/project/path/file/path/TEST_MAPPING', 'w') as f: f.write('not test mapping') optimizer = self._create_general_tests_optimizer() with self.assertRaises(json.decoder.JSONDecodeError): build_targets = optimizer.get_build_targets() @mock.patch('subprocess.run') def test_packaging_outputs_success(self, subprocess_run): subprocess_run.return_value = self._get_soong_vars_output() optimizer = self._create_general_tests_optimizer() self._set_up_build_outputs(['test_mapping_module']) targets = optimizer.get_build_targets() package_commands = optimizer.get_package_outputs_commands() self._verify_soong_zip_commands(package_commands, ['test_mapping_module']) @mock.patch('subprocess.run') def test_get_soong_dumpvars_fails_raises(self, subprocess_run): subprocess_run.return_value = self._get_soong_vars_output(return_code=-1) optimizer = self._create_general_tests_optimizer() self._set_up_build_outputs(['test_mapping_module']) targets = optimizer.get_build_targets() with self.assertRaisesRegex(RuntimeError, 'Soong dumpvars failed!'): package_commands = optimizer.get_package_outputs_commands() @mock.patch('subprocess.run') def test_get_soong_dumpvars_bad_output_raises(self, subprocess_run): subprocess_run.return_value = self._get_soong_vars_output( stdout='This output is bad' ) optimizer = self._create_general_tests_optimizer() self._set_up_build_outputs(['test_mapping_module']) targets = optimizer.get_build_targets() with self.assertRaisesRegex( RuntimeError, 'Error parsing soong dumpvars output' ): package_commands = optimizer.get_package_outputs_commands() def _create_general_tests_optimizer(self, build_context: BuildContext = None): if not build_context: build_context = self._create_build_context() return optimized_targets.GeneralTestsOptimizer( 'general-tests', build_context, None ) def _create_build_context( self, general_tests_optimized: bool = True, test_context: dict[str, any] = None, ) -> BuildContext: if not test_context: test_context = self._create_test_context() build_context_dict = {} build_context_dict['enabledBuildFeatures'] = [{'name': 'optimized_build'}] if general_tests_optimized: build_context_dict['enabledBuildFeatures'].append( {'name': 'general_tests_optimized'} ) build_context_dict['testContext'] = test_context return BuildContext(build_context_dict) def _create_test_context(self): return { 'testInfos': [ { 'name': 'atp_test', 'target': 'test_target', 'branch': 'branch', 'extraOptions': [ { 'key': 'additional-files-filter', 'values': ['general-tests.zip'], }, { 'key': 'test-mapping-test-group', 'values': ['test-mapping-group'], }, ], 'command': '/tf/command', 'extraBuildTargets': [ 'extra_build_target', ], }, ], } def _get_soong_vars_output( self, return_code: int = 0, stdout: str = '' ) -> subprocess.CompletedProcess: return_value = subprocess.CompletedProcess(args=[], returncode=return_code) if not stdout: stdout = textwrap.dedent(f"""\ HOST_OUT_TESTCASES='{self._host_out_testcases}' TARGET_OUT_TESTCASES='{self._target_out_testcases}' PRODUCT_OUT='{self._product_out}' SOONG_HOST_OUT='{self._soong_host_out}' HOST_OUT='{self._host_out}'""") return_value.stdout = stdout return return_value def _set_up_build_outputs(self, targets: list[str]): for target in targets: host_dir = self._host_out_testcases / target host_dir.mkdir() (host_dir / f'{target}.config').touch() (host_dir / f'test_file').touch() target_dir = self._target_out_testcases / target target_dir.mkdir() (target_dir / f'{target}.config').touch() (target_dir / f'test_file').touch() def _verify_soong_zip_commands(self, commands: list[str], targets: list[str]): """Verify the structure of the zip commands. Zip commands have to start with the soong_zip binary path, then are followed by a couple of options and the name of the file being zipped. Depending on which zip we are creating look for a few essential items being added in those zips. Args: commands: list of command lists targets: list of targets expected to be in general-tests.zip """ for command in commands: self.assertEqual( '/tmp/top/prebuilts/build-tools/linux-x86/bin/soong_zip', command[0], ) self.assertEqual('-d', command[1]) self.assertEqual('-o', command[2]) match (command[3]): case '/tmp/top/out/dist/general-tests_configs.zip': self.assertIn(f'{self._host_out}/host_general-tests_list', command) self.assertIn( f'{self._product_out}/target_general-tests_list', command ) return case '/tmp/top/out/dist/general-tests_list.zip': self.assertIn('-f', command) self.assertIn(f'{self._host_out}/general-tests_list', command) return case '/tmp/top/out/dist/general-tests.zip': for target in targets: self.assertIn(f'{self._host_out_testcases}/{target}', command) self.assertIn(f'{self._target_out_testcases}/{target}', command) self.assertIn( f'{self._soong_host_out}/framework/cts-tradefed.jar', command ) self.assertIn( f'{self._soong_host_out}/framework/compatibility-host-util.jar', command, ) self.assertIn( f'{self._soong_host_out}/framework/vts-tradefed.jar', command ) return case _: self.fail(f'malformed command: {command}') if __name__ == '__main__': # Setup logging to be silent so unit tests can pass through TF. logging.disable(logging.ERROR) unittest.main() ================================================ FILE: ci/test_discovery_agent.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test discovery agent that uses TradeFed to discover test artifacts.""" import glob import json import logging import os import subprocess class TestDiscoveryAgent: """Test discovery agent.""" _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = ( "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/" ) _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery" _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes" _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt" def __init__( self, tradefed_args: list[str], test_mapping_zip_path: str = "", tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH, ): self.tradefed_args = tradefed_args self.test_mapping_zip_path = test_mapping_zip_path self.tradefed_jar_relevant_files_path = tradefed_jar_revelant_files_path def discover_test_zip_regexes(self) -> list[str]: """Discover test zip regexes from TradeFed. Returns: A list of test zip regexes that TF is going to try to pull files from. """ test_discovery_output_file_name = os.path.join( os.environ.get('TOP'), 'out', self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME ) with open( test_discovery_output_file_name, mode="w+t" ) as test_discovery_output_file: java_args = [] java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java") java_args.append("-cp") java_args.append( self.create_classpath(self.tradefed_jar_relevant_files_path) ) java_args.append( "com.android.tradefed.observatory.TestZipDiscoveryExecutor" ) java_args.extend(self.tradefed_args) env = os.environ.copy() env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name}) logging.info(f"Calling test discovery with args: {java_args}") try: result = subprocess.run(args=java_args, env=env, text=True, check=True) logging.info(f"Test zip discovery output: {result.stdout}") except subprocess.CalledProcessError as e: raise TestDiscoveryError( f"Failed to run test discovery, strout: {e.stdout}, strerr:" f" {e.stderr}, returncode: {e.returncode}" ) data = json.loads(test_discovery_output_file.read()) logging.info(f"Test discovery result file content: {data}") if ( self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY] ): raise TestDiscoveryError("No possible test discovery") if ( data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is [] ): raise TestDiscoveryError("No test zip regexes returned") return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] def discover_test_modules(self) -> list[str]: """Discover test modules from TradeFed. Returns: A list of test modules that TradeFed is going to execute based on the TradeFed test args. """ return [] def create_classpath(self, directory): """Creates a classpath string from all .jar files in the given directory. Args: directory: The directory to search for .jar files. Returns: A string representing the classpath, with jar files separated by the OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows). """ jar_files = glob.glob(os.path.join(directory, "*.jar")) return os.pathsep.join(jar_files) class TestDiscoveryError(Exception): """A TestDiscoveryErrorclass.""" def __init__(self, message): super().__init__(message) self.message = message ================================================ FILE: ci/test_mapping_module_retriever.py ================================================ # Copyright 2024, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Simple parsing code to scan test_mapping files and determine which modules are needed to build for the given list of changed files. TODO(lucafarsi): Deduplicate from artifact_helper.py """ # TODO(lucafarsi): Share this logic with the original logic in # test_mapping_test_retriever.py import json import os import re from typing import Any # Regex to extra test name from the path of test config file. TEST_NAME_REGEX = r'(?:^|.*/)([^/]+)\.config' # Key name for TEST_MAPPING imports KEY_IMPORTS = 'imports' KEY_IMPORT_PATH = 'path' # Name of TEST_MAPPING file. TEST_MAPPING = 'TEST_MAPPING' # Pattern used to identify double-quoted strings and '//'-format comments in # TEST_MAPPING file, but only double-quoted strings are included within the # matching group. _COMMENTS_RE = re.compile(r'(\"(?:[^\"\\]|\\.)*\"|(?=//))(?://.*)?') def FilterComments(test_mapping_file: str) -> str: """Remove comments in TEST_MAPPING file to valid format. Only '//' is regarded as comments. Args: test_mapping_file: Path to a TEST_MAPPING file. Returns: Valid json string without comments. """ return re.sub(_COMMENTS_RE, r'\1', test_mapping_file) def GetTestMappings(paths: set[str], checked_paths: set[str]) -> dict[str, dict[str, Any]]: """Get the affected TEST_MAPPING files. TEST_MAPPING files in source code are packaged into a build artifact `test_mappings.zip`. Inside the zip file, the path of each TEST_MAPPING file is preserved. From all TEST_MAPPING files in the source code, this method locates the affected TEST_MAPPING files based on the given paths list. A TEST_MAPPING file may also contain `imports` that import TEST_MAPPING files from a different location, e.g., "imports": [ { "path": "../folder2" } ] In that example, TEST_MAPPING files inside ../folder2 (relative to the TEST_MAPPING file containing that imports section) and its parent directories will also be included. Args: paths: A set of paths with related TEST_MAPPING files for given changes. checked_paths: A set of paths that have been checked for TEST_MAPPING file already. The set is updated after processing each TEST_MAPPING file. It's used to prevent infinite loop when the method is called recursively. Returns: A dictionary of Test Mapping containing the content of the affected TEST_MAPPING files, indexed by the path containing the TEST_MAPPING file. """ test_mappings = {} # Search for TEST_MAPPING files in each modified path and its parent # directories. all_paths = set() for path in paths: dir_names = path.split(os.path.sep) all_paths |= set( [os.path.sep.join(dir_names[:i + 1]) for i in range(len(dir_names))]) # Add root directory to the paths to search for TEST_MAPPING file. all_paths.add('') all_paths.difference_update(checked_paths) checked_paths |= all_paths # Try to load TEST_MAPPING file in each possible path. for path in all_paths: try: test_mapping_file = os.path.join(os.path.join(os.getcwd(), path), 'TEST_MAPPING') # Read content of TEST_MAPPING file. content = FilterComments(open(test_mapping_file, "r").read()) test_mapping = json.loads(content) test_mappings[path] = test_mapping import_paths = set() for import_detail in test_mapping.get(KEY_IMPORTS, []): import_path = import_detail[KEY_IMPORT_PATH] # Try the import path as absolute path. import_paths.add(import_path) # Try the import path as relative path based on the test mapping file # containing the import. norm_import_path = os.path.normpath(os.path.join(path, import_path)) import_paths.add(norm_import_path) import_paths.difference_update(checked_paths) if import_paths: import_test_mappings = GetTestMappings(import_paths, checked_paths) test_mappings.update(import_test_mappings) except (KeyError, FileNotFoundError, NotADirectoryError): # TEST_MAPPING file doesn't exist in path pass return test_mappings def FindAffectedModules( test_mappings: dict[str, Any], changed_files: set[str], test_mapping_test_groups: set[str], ) -> set[str]: """Find affected test modules. Find the affected set of test modules that would run in a test mapping run based on the given test mappings, changed files, and test mapping test group. Args: test_mappings: A set of test mappings returned by GetTestMappings in the following format: { 'test_mapping_file_path': { 'group_name' : [ 'name': 'module_name', ], } } changed_files: A set of files changed for the given run. test_mapping_test_groups: A set of test mapping test groups that are being considered for the given run. Returns: A set of test module names which would run for a test mapping test run with the given parameters. """ modules = set() for test_mapping in test_mappings.values(): for group_name, group in test_mapping.items(): # If a module is not in any of the test mapping groups being tested skip # it. if group_name not in test_mapping_test_groups: continue for entry in group: module_name = entry.get('name') if not module_name: continue file_patterns = entry.get('file_patterns') if not file_patterns: modules.add(module_name) continue if matches_file_patterns(file_patterns, changed_files): modules.add(module_name) return modules def MatchesFilePatterns( file_patterns: list[set], changed_files: set[str] ) -> bool: """Checks if any of the changed files match any of the file patterns. Args: file_patterns: A list of file patterns to match against. changed_files: A set of files to check against the file patterns. Returns: True if any of the changed files match any of the file patterns. """ return any(re.search(pattern, "|".join(changed_files)) for pattern in file_patterns) ================================================ FILE: common/core.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Only use ANDROID_BUILD_SHELL to wrap around bash. # DO NOT use other shells such as zsh. ifdef ANDROID_BUILD_SHELL SHELL := $(ANDROID_BUILD_SHELL) else # Use bash, not whatever shell somebody has installed as /bin/sh # This is repeated from main.mk, since envsetup.sh runs this file # directly. SHELL := /bin/bash endif # Utility variables. empty := space := $(empty) $(empty) comma := , # Note that make will eat the newline just before endef. define newline endef # The pound character "#" define pound # endef # Unfortunately you can't simply define backslash as \ or \\. backslash := \a backslash := $(patsubst %a,%,$(backslash)) TOP :=$= . TOPDIR :=$= # Prevent accidentally changing these variables .KATI_READONLY := SHELL empty space comma newline pound backslash # Basic warning/error wrappers. These will be redefined to include the local # module information when reading Android.mk files. define pretty-warning $(warning $(1)) endef define pretty-error $(error $(1)) endef ================================================ FILE: common/json.mk ================================================ 4space :=$= $(space)$(space)$(space)$(space) invert_bool =$= $(if $(strip $(1)),,true) # Converts a list to a JSON list. # $1: List separator. # $2: List. _json_list =$= [$(if $(2),"$(subst $(1),"$(comma)",$(2))")] # Converts a space-separated list to a JSON list. json_list =$= $(call _json_list,$(space),$(1)) # Converts a comma-separated list to a JSON list. csv_to_json_list =$= $(call _json_list,$(comma),$(1)) # Adds or removes 4 spaces from _json_indent json_increase_indent =$= $(eval _json_indent := $$(_json_indent)$$(4space)) json_decrease_indent =$= $(eval _json_indent := $$(subst _,$$(space),$$(patsubst %____,%,$$(subst $$(space),_,$$(_json_indent))))) # 1: Key name # 2: Value add_json_val =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": $$(strip $$(2))$$(comma)$$(newline)) add_json_str =$= $(call add_json_val,$(1),"$(strip $(2))") add_json_list =$= $(call add_json_val,$(1),$(call json_list,$(patsubst %,%,$(2)))) add_json_csv =$= $(call add_json_val,$(1),$(call csv_to_json_list,$(strip $(2)))) add_json_bool =$= $(call add_json_val,$(1),$(if $(strip $(2)),true,false)) add_json_map =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": {$$(newline))$(json_increase_indent) add_json_map_anon =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent){$$(newline))$(json_increase_indent) end_json_map =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)},$$(newline)) add_json_array =$= $(eval _json_contents := $$(_json_contents)$$(_json_indent)"$$(strip $$(1))": [$$(newline))$(json_increase_indent) end_json_array =$= $(json_decrease_indent)$(eval _json_contents := $$(_json_contents)$$(if $$(filter %$$(comma),$$(lastword $$(_json_contents))),__SV_END)$$(_json_indent)],$$(newline)) # Clears _json_contents to start a new json file json_start =$= $(eval _json_contents := {$$(newline))$(eval _json_indent := $$(4space)) # Adds the trailing close brace to _json_contents, and removes any trailing commas if necessary json_end =$= $(eval _json_contents := $$(subst $$(comma)$$(newline)__SV_END,$$(newline),$$(_json_contents)__SV_END}$$(newline))) json_contents =$= $(_json_contents) ================================================ FILE: common/math.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ########################################################### # Basic math functions for non-negative integers <= 100 # # (SDK versions for example) ########################################################### __MATH_POS_NUMBERS := 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \ 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 __MATH_NUMBERS := 0 $(__MATH_POS_NUMBERS) __MATH_ONE_NUMBERS := 0 1 2 3 4 5 6 7 8 9 math-error = $(call pretty-error,$(1)) math-expect := math-expect-true := math-expect := math-expect-error := # Run the math tests with: # make -f ${ANDROID_BUILD_TOP}/build/make/common/math.mk RUN_MATH_TESTS=true # $(get_build_var CKATI) -f ${ANDROID_BUILD_TOP}//build/make/common/math.mk RUN_MATH_TESTS=true ifdef RUN_MATH_TESTS ifndef empty empty := space := $(empty) $(empty) endif MATH_TEST_FAILURE := MATH_TEST_ERROR := math-error = $(if $(MATH_TEST_ERROR),,$(eval MATH_TEST_ERROR:=$(1))) define math-expect $(eval got:=$$$1) \ $(if $(subst $(got),,$(2))$(subst $(2),,$(got))$(MATH_TEST_ERROR), \ $(if $(MATH_TEST_ERROR),$(warning $(MATH_TEST_ERROR)),$(warning $$$1 '$(got)' != '$(2)')) \ $(eval MATH_TEST_FAILURE := true)) \ $(eval MATH_TEST_ERROR :=) \ $(eval got:=) endef math-expect-true = $(call math-expect,$(1),true) math-expect-false = $(call math-expect,$(1),) define math-expect-error $(eval got:=$$$1) \ $(if $(subst $(MATH_TEST_ERROR),,$(2))$(subst $(2),,$(MATH_TEST_ERROR)), \ $(warning '$(MATH_TEST_ERROR)' != '$(2)') \ $(eval MATH_TEST_FAILURE := true)) \ $(eval MATH_TEST_ERROR :=) \ $(eval got:=) endef endif # Returns true if $(1) is a non-negative integer <= 100, otherwise returns nothing. define math_is_number_in_100 $(strip \ $(if $(1),,$(call math-error,Argument missing)) \ $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \ $(if $(filter $(1),$(__MATH_NUMBERS)),true)) endef # Same with math_is_number_in_100, but no limit. define _math_ext_is_number $(strip \ $(if $(1),,$(call math-error,Argument missing)) \ $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \ $(eval should_empty:=$(1)) \ $(foreach num,$(__MATH_ONE_NUMBERS),\ $(eval should_empty:=$(subst $(num),$(empty),$(should_empty)))) \ $(if $(should_empty),,true)) endef # Returns true if $(1) is a non-negative integer. define math_is_number $(strip $(if $(call math_is_number_in_100,$(1)),true,$(call _math_ext_is_number,$(1)))) endef # Returns true if $(1) is a positive or negative integer. define math_is_int $(call math_is_number,$(patsubst -%,%,$(1))) endef define math_is_zero $(strip \ $(if $(word 2,$(1)),$(call math-error,Multiple words in a single argument: $(1))) \ $(if $(filter 0,$(1)),true)) endef $(call math-expect-true,(call math_is_number,0)) $(call math-expect-true,(call math_is_number,2)) $(call math-expect-true,(call math_is_number,202412)) $(call math-expect-false,(call math_is_number,foo)) $(call math-expect-false,(call math_is_number,-1)) $(call math-expect-true,(call math_is_int,50)) $(call math-expect-true,(call math_is_int,-1)) $(call math-expect-true,(call math_is_int,-528)) $(call math-expect-true,(call math_is_int,-0)) $(call math-expect-false,(call math_is_int,--1)) $(call math-expect-false,(call math_is_int,-)) $(call math-expect-error,(call math_is_number,1 2),Multiple words in a single argument: 1 2) $(call math-expect-error,(call math_is_number,no 2),Multiple words in a single argument: no 2) $(call math-expect-true,(call math_is_zero,0)) $(call math-expect-false,(call math_is_zero,1)) $(call math-expect-false,(call math_is_zero,foo)) $(call math-expect-error,(call math_is_zero,1 2),Multiple words in a single argument: 1 2) $(call math-expect-error,(call math_is_zero,no 2),Multiple words in a single argument: no 2) define _math_check_valid $(if $(call math_is_number_in_100,$(1)),,$(call math-error,Only non-negative integers <= 100 are supported (not $(1)))) endef $(call math-expect,(call _math_check_valid,0)) $(call math-expect,(call _math_check_valid,1)) $(call math-expect,(call _math_check_valid,100)) $(call math-expect-error,(call _math_check_valid,-1),Only non-negative integers <= 100 are supported (not -1)) $(call math-expect-error,(call _math_check_valid,101),Only non-negative integers <= 100 are supported (not 101)) $(call math-expect-error,(call _math_check_valid,),Argument missing) $(call math-expect-error,(call _math_check_valid,1 2),Multiple words in a single argument: 1 2) # return a list containing integers ranging from [$(1),$(2)] define int_range_list $(strip \ $(call _math_check_valid,$(1))$(call _math_check_valid,$(2)) \ $(if $(call math_is_zero,$(1)),0)\ $(wordlist $(if $(call math_is_zero,$(1)),1,$(1)),$(2),$(__MATH_POS_NUMBERS))) endef $(call math-expect,(call int_range_list,0,1),0 1) $(call math-expect,(call int_range_list,1,1),1) $(call math-expect,(call int_range_list,1,2),1 2) $(call math-expect,(call int_range_list,2,1),) $(call math-expect-error,(call int_range_list,1,101),Only non-negative integers <= 100 are supported (not 101)) # Split an integer into a list of digits define _math_number_to_list $(strip \ $(if $(call _math_ext_is_number,$(1)),,\ $(call math-error,Only non-negative integers are supported (not $(1)))) \ $(eval num_list:=$(1)) \ $(foreach num,$(__MATH_ONE_NUMBERS),\ $(eval num_list:=$(subst $(num),$(space)$(num),$(num_list)))) \ $(if $(filter $(words $(num_list)),$(__MATH_ONE_NUMBERS)),,\ $(call math-error,Only non-negative integers with less than 9 digits are supported (not $(1)))) \ $(if $(filter 0,$(word 1,$(num_list))),\ $(call math-error,Only non-negative integers without leading zeros are supported (not $(1)))) \ $(num_list)) endef $(call math-expect,(call _math_number_to_list,123),1 2 3) $(call math-expect-error,(call _math_number_to_list,123 456),Multiple words in a single argument: 123 456) $(call math-expect-error,(call _math_number_to_list,-123),Only non-negative integers are supported (not -123)) $(call math-expect-error,(call _math_number_to_list,002),Only non-negative integers without leading zeros are supported (not 002)) $(call math-expect-error,(call _math_number_to_list,1234567890),Only non-negative integers with less than 9 digits are supported (not 1234567890)) # Compare 1-digit integer $(1) and $(2). # Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals. define _math_1digit_comp $(strip \ $(if $(filter $(1),$(2)),,\ $(if $(filter $(1),$(firstword $(filter $(1) $(2),$(__MATH_ONE_NUMBERS)))),-1,1))) endef $(call math-expect,(call _math_1digit_comp,1,1)) $(call math-expect,(call _math_1digit_comp,0,9),-1) $(call math-expect,(call _math_1digit_comp,3,1),1) # Compare the same $(3)-digit-length integers $(1) and $(2) that are split into a list of digits. # Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals. define _math_list_comp $(strip \ $(eval ans:=) \ $(foreach num,$(call int_range_list,1,$(3)),\ $(if $(ans),,$(eval ans:=$(call _math_1digit_comp,$(word $(num),$(1)),$(word $(num),$(2)))))) \ $(ans)) endef # Compare any two non-negative integers $(1) and $(2). # Returns 1 if $(1) > $(2), -1 if $(1) < $(2), nothing if equals. define _math_ext_comp $(strip \ $(eval num_list1:=$(call _math_number_to_list,$(1))) \ $(eval len1:=$(words $(num_list1))) \ $(eval num_list2:=$(call _math_number_to_list,$(2))) \ $(eval len2:=$(words $(num_list2))) \ $(eval comp:=$(call _math_1digit_comp,$(len1),$(len2))) \ $(if $(comp),$(comp),$(call _math_list_comp,$(num_list1),$(num_list2),$(len1)))) endef $(call math-expect,(call _math_ext_comp,5,10),-1) $(call math-expect,(call _math_ext_comp,12345,12345)) $(call math-expect,(call _math_ext_comp,500,5),1) $(call math-expect,(call _math_ext_comp,202404,202504),-1) # Returns the greater of $1 or $2. # If $1 or $2 is not a positive integer, then an error is generated. define math_max $(strip \ $(if $(filter truetrue,$(call math_is_number_in_100,$(1))$(call math_is_number_in_100,$(2))),\ $(lastword $(filter $(1) $(2),$(__MATH_NUMBERS))),\ $(if $(filter 1,$(call _math_ext_comp,$(1),$(2))),$(1),$(2)))) endef # Returns the lesser of $1 or $2. define math_min $(strip \ $(if $(filter truetrue,$(call math_is_number_in_100,$(1))$(call math_is_number_in_100,$(2))),\ $(firstword $(filter $(1) $(2),$(__MATH_NUMBERS))),\ $(if $(filter -1,$(call _math_ext_comp,$(1),$(2))),$(1),$(2)))) endef $(call math-expect-error,(call math_max),Argument missing) $(call math-expect-error,(call math_max,1),Argument missing) $(call math-expect-error,(call math_max,1 2,3),Multiple words in a single argument: 1 2) $(call math-expect-error,(call math_min,1,2 3),Multiple words in a single argument: 2 3) $(call math-expect,(call math_max,0,1),1) $(call math-expect,(call math_max,1,0),1) $(call math-expect,(call math_max,1,1),1) $(call math-expect,(call math_max,5,42),42) $(call math-expect,(call math_max,42,5),42) $(call math-expect,(call math_min,0,1),0) $(call math-expect,(call math_min,1,0),0) $(call math-expect,(call math_min,1,1),1) $(call math-expect,(call math_min,7,32),7) $(call math-expect,(call math_min,32,7),7) $(call math-expect,(call math_max,32759,7),32759) $(call math-expect,(call math_max,7,32759),32759) $(call math-expect,(call math_max,202404,202505),202505) $(call math-expect,(call math_max,202404,202404),202404) $(call math-expect,(call math_min,8908527,32),32) $(call math-expect,(call math_min,32,8908527),32) $(call math-expect,(call math_min,202404,202505),202404) $(call math-expect,(call math_min,202404,202404),202404) define math_gt_or_eq $(if $(filter $(1),$(call math_max,$(1),$(2))),true) endef define math_gt $(if $(call math_gt_or_eq,$(2),$(1)),,true) endef define math_lt_or_eq $(if $(call math_gt_or_eq,$(2),$(1)),true) endef define math_lt $(if $(call math_gt_or_eq,$(1),$(2)),,true) endef $(call math-expect-true,(call math_gt_or_eq, 2, 1)) $(call math-expect-true,(call math_gt_or_eq, 1, 1)) $(call math-expect-false,(call math_gt_or_eq, 1, 2)) $(call math-expect-true,(call math_gt, 4, 3)) $(call math-expect-false,(call math_gt, 5, 5)) $(call math-expect-false,(call math_gt, 6, 7)) $(call math-expect-true,(call math_lt_or_eq, 11, 11)) $(call math-expect-false,(call math_lt_or_eq, 25, 15)) $(call math-expect-true,(call math_lt_or_eq, 9, 16)) $(call math-expect-false,(call math_lt, 1, 0)) $(call math-expect-false,(call math_lt, 8, 8)) $(call math-expect-true,(call math_lt, 10, 11)) $(call math-expect-true,(call math_gt_or_eq, 2573904, 2573900)) $(call math-expect-true,(call math_gt_or_eq, 12345, 12345)) $(call math-expect-false,(call math_gt_or_eq, 56, 2780)) # $1 is the variable name to increment define inc_and_print $(strip $(eval $(1) := $($(1)) .)$(words $($(1)))) endef ifdef RUN_MATH_TESTS a := $(call math-expect,(call inc_and_print,a),1) $(call math-expect,(call inc_and_print,a),2) $(call math-expect,(call inc_and_print,a),3) $(call math-expect,(call inc_and_print,a),4) endif # Returns the words in $2 that are numbers and are less than $1 define numbers_less_than $(strip \ $(foreach n,$2, \ $(if $(call math_is_number,$(n)), \ $(if $(call math_lt,$(n),$(1)), \ $(n))))) endef $(call math-expect,(call numbers_less_than,0,0 1 2 3),) $(call math-expect,(call numbers_less_than,1,0 2 1 3),0) $(call math-expect,(call numbers_less_than,2,0 2 1 3),0 1) $(call math-expect,(call numbers_less_than,3,0 2 1 3),0 2 1) $(call math-expect,(call numbers_less_than,4,0 2 1 3),0 2 1 3) $(call math-expect,(call numbers_less_than,3,0 2 1 3 2),0 2 1 2) $(call math-expect,(call numbers_less_than,100,0 1000 50 101 100),0 50) # Returns the words in $2 that are numbers and are greater or equal to $1 define numbers_greater_or_equal_to $(strip \ $(foreach n,$2, \ $(if $(call math_is_number,$(n)), \ $(if $(call math_gt_or_eq,$(n),$(1)), \ $(n))))) endef $(call math-expect,(call numbers_greater_or_equal_to,4,0 1 2 3),) $(call math-expect,(call numbers_greater_or_equal_to,3,0 2 1 3),3) $(call math-expect,(call numbers_greater_or_equal_to,2,0 2 1 3),2 3) $(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3),2 1 3) $(call math-expect,(call numbers_greater_or_equal_to,0,0 2 1 3),0 2 1 3) $(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3 2),2 1 3 2) # 10,001 = 10 ** 4 + 1, contains 10,001 x's, so 1 more than 10,000 (future) API level _INT_LIMIT_WORDS := x $(foreach a,0 1 2 3 4 5 6 7 8 9,$(foreach b,0 1 2 3 4 5 6 7 8 9,\ $(foreach c,0 1 2 3 4 5 6 7 8 9,x x x x x x x x x x))) define _int_encode $(if $(filter $(words x $(_INT_LIMIT_WORDS)),$(words $(wordlist 1,$(1),x $(_INT_LIMIT_WORDS)))),\ $(call math-error,integer greater than $(words $(_INT_LIMIT_WORDS)) is not supported!),\ $(wordlist 1,$(1),$(_INT_LIMIT_WORDS))) endef # _int_max returns the maximum of the two arguments # input: two (x) lists; output: one (x) list # integer cannot be passed in directly. It has to be converted using _int_encode. define _int_max $(subst xx,x,$(join $(1),$(2))) endef # first argument is greater than second argument # output: non-empty if true # integer cannot be passed in directly. It has to be converted using _int_encode. define _int_greater-than $(filter-out $(words $(2)),$(words $(call _int_max,$(1),$(2)))) endef # first argument equals to second argument # output: non-empty if true # integer cannot be passed in directly. It has to be converted using _int_encode. define _int_equal $(filter $(words $(1)),$(words $(2))) endef # first argument is greater than or equal to second argument # output: non-empty if true # integer cannot be passed in directly. It has to be converted using _int_encode. define _int_greater-or-equal $(call _int_greater-than,$(1),$(2))$(call _int_equal,$(1),$(2)) endef define int_plus $(words $(call _int_encode,$(1)) $(call _int_encode,$(2))) endef $(call math-expect,(call int_plus,0,0),0) $(call math-expect,(call int_plus,0,1),1) $(call math-expect,(call int_plus,1,0),1) $(call math-expect,(call int_plus,1,100),101) $(call math-expect,(call int_plus,100,100),200) define int_subtract $(strip \ $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))),\ $(words $(filter-out xx,$(join $(call _int_encode,$(1)),$(call _int_encode,$(2))))),\ $(call math-error,subtract underflow $(1) - $(2)))) endef $(call math-expect,(call int_subtract,0,0),0) $(call math-expect,(call int_subtract,1,0),1) $(call math-expect,(call int_subtract,1,1),0) $(call math-expect,(call int_subtract,100,1),99) $(call math-expect,(call int_subtract,200,100),100) $(call math-expect-error,(call int_subtract,0,1),subtract underflow 0 - 1) define int_multiply $(words $(foreach a,$(call _int_encode,$(1)),$(call _int_encode,$(2)))) endef $(call math-expect,(call int_multiply,0,0),0) $(call math-expect,(call int_multiply,1,0),0) $(call math-expect,(call int_multiply,1,1),1) $(call math-expect,(call int_multiply,100,1),100) $(call math-expect,(call int_multiply,1,100),100) $(call math-expect,(call int_multiply,4,100),400) $(call math-expect,(call int_multiply,100,4),400) define int_divide $(if $(filter 0,$(2)),$(call math-error,division by zero is not allowed!),$(strip \ $(if $(call _int_greater-or-equal,$(call _int_encode,$(1)),$(call _int_encode,$(2))), \ $(call int_plus,$(call int_divide,$(call int_subtract,$(1),$(2)),$(2)),1),0))) endef $(call math-expect,(call int_divide,1,1),1) $(call math-expect,(call int_divide,200,1),200) $(call math-expect,(call int_divide,200,3),66) $(call math-expect,(call int_divide,1,2),0) $(call math-expect-error,(call int_divide,0,0),division by zero is not allowed!) $(call math-expect-error,(call int_divide,1,0),division by zero is not allowed!) ifdef RUN_MATH_TESTS ifdef MATH_TEST_FAILURE math-tests: @echo FAIL @false else math-tests: @echo PASS endif .PHONY: math-tests endif ================================================ FILE: common/strings.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ########################################################### ## Convert to lower case without requiring a shell, which isn't cacheable. ## ## $(1): string ########################################################### to-lower=$(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1)))))))))))))))))))))))))) ########################################################### ## Convert to upper case without requiring a shell, which isn't cacheable. ## ## $(1): string ########################################################### to-upper=$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1)))))))))))))))))))))))))) # Test to-lower and to-upper lower := abcdefghijklmnopqrstuvwxyz-_ upper := ABCDEFGHIJKLMNOPQRSTUVWXYZ-_ ifneq ($(lower),$(call to-lower,$(upper))) $(error to-lower sanity check failure) endif ifneq ($(upper),$(call to-upper,$(lower))) $(error to-upper sanity check failure) endif lower := upper := ########################################################### ## Returns true if $(1) and $(2) are equal. Returns ## the empty string if they are not equal. ########################################################### define streq $(strip $(if $(strip $(1)),\ $(if $(strip $(2)),\ $(if $(filter-out __,_$(subst $(strip $(1)),,$(strip $(2)))$(subst $(strip $(2)),,$(strip $(1)))_),,true), \ ),\ $(if $(strip $(2)),\ ,\ true)\ )) endef ########################################################### ## Convert "a b c" into "a:b:c" ########################################################### define normalize-path-list $(subst $(space),:,$(strip $(1))) endef ########################################################### ## Convert "a b c" into "a,b,c" ########################################################### define normalize-comma-list $(subst $(space),$(comma),$(strip $(1))) endef ########################################################### ## Read the word out of a colon-separated list of words. ## This has the same behavior as the built-in function ## $(word n,str). ## ## The individual words may not contain spaces. ## ## $(1): 1 based index ## $(2): value of the form a:b:c... ########################################################### define word-colon $(word $(1),$(subst :,$(space),$(2))) endef ########################################################### ## Read a colon-separated sublist out of a colon-separated ## list of words. ## This has similar behavior to the built-in function ## $(wordlist s,e,str) except both the input and output ## word lists are colon-separated. ## ## The individual words may not contain spaces. ## ## $(1): 1 based index start ## $(2): 1 based index end (can be 0) ## $(3): value of the form a:b:c... ########################################################### define wordlist-colon $(subst $(space),:,$(wordlist $(1),$(2),$(subst :,$(space),$(3)))) endef ########################################################### ## Convert "a=b c= d e = f = g h=" into "a=b c=d e= f=g h=" ## ## $(1): list to collapse ## $(2): if set, separator word; usually "=", ":", or ":=" ## Defaults to "=" if not set. ########################################################### define collapse-pairs $(strip \ $(eval _cpSEP := $(strip $(if $(2),$(2),=)))\ $(eval _cpLHS :=)\ $(eval _cpRET :=)\ $(foreach w,$(subst $(space)$(_cpSEP),$(_cpSEP),$(strip \ $(subst $(_cpSEP),$(space)$(_cpSEP)$(space),$(1)))),\ $(if $(findstring $(_cpSEP),$(w)),\ $(eval _cpRET += $(_cpLHS))$(eval _cpLHS := $(w)),\ $(eval _cpRET += $(_cpLHS)$(w))$(eval _cpLHS :=)))\ $(if $(_cpLHS),$(_cpRET)$(space)$(_cpLHS),$(_cpRET))\ $(eval _cpSEP :=)\ $(eval _cpLHS :=)\ $(eval _cpRET :=)) endef # Sanity check for collapse-pairs. ifneq (a=b c=d e= f=g h=,$(call collapse-pairs,a=b c= d e = f = g h=)) $(error collapse-pairs sanity check failure) endif ifneq (a:=b c:=d e:=f g:=h,$(call collapse-pairs,a:=b c:= d e :=f g := h,:=)) $(error collapse-pairs sanity check failure) endif ########################################################### ## Given a list of pairs, if multiple pairs have the same ## first components, keep only the first pair. ## ## $(1): list of pairs ## $(2): the separator word, such as ":", "=", etc. define uniq-pairs-by-first-component $(eval _upbfc_fc_set :=)\ $(strip $(foreach w,$(1), $(eval _first := $(word 1,$(subst $(2),$(space),$(w))))\ $(if $(filter $(_upbfc_fc_set),$(_first)),,$(w)\ $(eval _upbfc_fc_set += $(_first)))))\ $(eval _upbfc_fc_set :=)\ $(eval _first:=) endef ================================================ FILE: core/LINUX_KERNEL_COPYING ================================================ NOTE! This copyright does *not* cover user programs that use kernel services by normal system calls - this is merely considered normal use of the kernel, and does *not* fall under the heading of "derived work". Also note that the GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the Linux kernel) is copyrighted by me and others who actually wrote it. Also note that the only valid version of the GPL as far as the kernel is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Linus Torvalds ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ================================================ FILE: core/Makefile ================================================ # Put some miscellaneous rules here # HACK: clear LOCAL_PATH from including last build target before calling # intermedites-dir-for LOCAL_PATH := $(BUILD_SYSTEM) SYSTEM_NOTICE_DEPS := VENDOR_NOTICE_DEPS := UNMOUNTED_NOTICE_DEPS := UNMOUNTED_NOTICE_VENDOR_DEPS := ODM_NOTICE_DEPS := OEM_NOTICE_DEPS := PRODUCT_NOTICE_DEPS := SYSTEM_EXT_NOTICE_DEPS := VENDOR_DLKM_NOTICE_DEPS := ODM_DLKM_NOTICE_DEPS := SYSTEM_DLKM_NOTICE_DEPS := # IMAGES_TO_BUILD is a list of the partition .img files that will be created. IMAGES_TO_BUILD:= ifneq ($(BUILDING_BOOT_IMAGE),) IMAGES_TO_BUILD += boot endif ifneq ($(BUILDING_CACHE_IMAGE),) IMAGES_TO_BUILD += cache endif ifneq ($(BUILDING_DEBUG_BOOT_IMAGE),) IMAGES_TO_BUILD += debug_boot endif ifneq ($(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),) IMAGES_TO_BUILD += debug_vendor_boot endif ifneq ($(BUILDING_INIT_BOOT_IMAGE),) IMAGES_TO_BUILD += init_boot endif ifneq ($(BUILDING_ODM_DLKM_IMAGE),) IMAGES_TO_BUILD += odm_dlkm endif ifneq ($(BUILDING_ODM_IMAGE),) IMAGES_TO_BUILD += odm endif ifneq ($(BUILDING_PRODUCT_IMAGE),) IMAGES_TO_BUILD += product endif ifneq ($(BUILDING_RAMDISK_IMAGE),) IMAGES_TO_BUILD += ramdisk endif ifneq ($(BUILDING_RECOVERY_IMAGE),) IMAGES_TO_BUILD += recovery endif ifneq ($(BUILDING_SUPER_EMPTY_IMAGE),) IMAGES_TO_BUILD += super_empty endif ifneq ($(BUILDING_SYSTEM_DLKM_IMAGE),) IMAGES_TO_BUILD += system_dlkm endif ifneq ($(BUILDING_SYSTEM_EXT_IMAGE),) IMAGES_TO_BUILD += system_ext endif ifneq ($(BUILDING_SYSTEM_IMAGE),) IMAGES_TO_BUILD += system endif ifneq ($(BUILDING_SYSTEM_OTHER_IMAGE),) IMAGES_TO_BUILD += system_other endif ifneq ($(BUILDING_USERDATA_IMAGE),) IMAGES_TO_BUILD += userdata endif ifneq ($(BUILDING_VBMETA_IMAGE),) IMAGES_TO_BUILD += vbmeta endif ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),) IMAGES_TO_BUILD += vendor_boot endif ifneq ($(BUILDING_VENDOR_DLKM_IMAGE),) IMAGES_TO_BUILD += vendor_dlkm endif ifneq ($(BUILDING_VENDOR_IMAGE),) IMAGES_TO_BUILD += vendor endif ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),) IMAGES_TO_BUILD += vendor_kernel_boot endif # Release & Aconfig Flags # ----------------------------------------------------------------- include $(BUILD_SYSTEM)/packaging/flags.mk # ----------------------------------------------------------------- # Define rules to copy PRODUCT_COPY_FILES defined by the product. # PRODUCT_COPY_FILES contains words like :[:]. # is relative to $(PRODUCT_OUT), so it should look like, # e.g., "system/etc/file.xml". # The filter part means "only eval the copy-one-file rule if this # src:dest pair is the first one to match the same dest" #$(1): the src:dest pair #$(2): the dest define check-product-copy-files $(if $(filter-out $(TARGET_COPY_OUT_SYSTEM_OTHER)/%,$(2)), \ $(if $(filter %.apk, $(2)),$(error \ Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!))) \ $(if $(filter true,$(BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES)),, \ $(if $(filter $(TARGET_COPY_OUT_SYSTEM)/etc/vintf/% \ $(TARGET_COPY_OUT_SYSTEM)/manifest.xml \ $(TARGET_COPY_OUT_SYSTEM)/compatibility_matrix.xml,$(2)), \ $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), use vintf_fragments instead!)) \ $(if $(filter $(TARGET_COPY_OUT_PRODUCT)/etc/vintf/%,$(2)), \ $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ use PRODUCT_MANIFEST_FILES / DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \ $(if $(filter $(TARGET_COPY_OUT_SYSTEM_EXT)/etc/vintf/%,$(2)), \ $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ use vintf_compatibility_matrix / vintf_fragments instead!)) \ $(if $(filter $(TARGET_COPY_OUT_VENDOR)/etc/vintf/% \ $(TARGET_COPY_OUT_VENDOR)/manifest.xml \ $(TARGET_COPY_OUT_VENDOR)/compatibility_matrix.xml,$(2)), \ $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ use DEVICE_MANIFEST_FILE / DEVICE_MATRIX_FILE / vintf_compatibility_matrix / vintf_fragments instead!)) \ $(if $(filter $(TARGET_COPY_OUT_ODM)/etc/vintf/% \ $(TARGET_COPY_OUT_ODM)/etc/manifest%,$(2)), \ $(error VINTF metadata found in PRODUCT_COPY_FILES: $(1), \ use ODM_MANIFEST_FILES / vintf_fragments instead!)) \ ) endef # Phony target to check PRODUCT_COPY_FILES copy pairs don't contain ELF files .PHONY: check-elf-prebuilt-product-copy-files check-elf-prebuilt-product-copy-files: check_elf_prebuilt_product_copy_files := true ifneq (,$(filter true,$(BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES))) check_elf_prebuilt_product_copy_files := endif check_elf_prebuilt_product_copy_files_hint := \ found ELF prebuilt in PRODUCT_COPY_FILES, use cc_prebuilt_binary / cc_prebuilt_library_shared instead. # filter out the duplicate : pairs. unique_product_copy_files_pairs := $(foreach cf,$(PRODUCT_COPY_FILES), \ $(if $(filter $(unique_product_copy_files_pairs),$(cf)),,\ $(eval unique_product_copy_files_pairs += $(cf)))) unique_product_copy_files_destinations := product_copy_files_ignored := $(foreach cf,$(unique_product_copy_files_pairs), \ $(eval _src := $(call word-colon,1,$(cf))) \ $(eval _dest := $(call word-colon,2,$(cf))) \ $(call check-product-copy-files,$(cf),$(_dest)) \ $(if $(filter $(unique_product_copy_files_destinations),$(_dest)), \ $(eval product_copy_files_ignored += $(cf)), \ $(eval _fulldest := $(call append-path,$(PRODUCT_OUT),$(_dest))) \ $(if $(filter %.xml,$(_dest)),\ $(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),\ $(if $(and $(filter %.jar,$(_dest)),$(filter $(basename $(notdir $(_dest))),$(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))),\ $(eval $(call copy-and-uncompress-dexs,$(_src),$(_fulldest))), \ $(if $(filter init%rc,$(notdir $(_dest)))$(filter %/etc/init/,$(dir $(_dest))),\ $(eval $(call copy-init-script-file-checked,$(_src),$(_fulldest))),\ $(if $(and $(filter true,$(check_elf_prebuilt_product_copy_files)), \ $(filter bin lib lib64,$(subst /,$(space),$(_dest)))), \ $(eval $(call copy-non-elf-file-checked,$(_src),$(_fulldest),$(check_elf_prebuilt_product_copy_files_hint))), \ $(eval $(call copy-one-file,$(_src),$(_fulldest))))))) \ $(eval unique_product_copy_files_destinations += $(_dest)))) # Dump a list of overriden (and ignored PRODUCT_COPY_FILES entries) pcf_ignored_file := $(PRODUCT_OUT)/product_copy_files_ignored.txt $(pcf_ignored_file): PRIVATE_IGNORED := $(sort $(product_copy_files_ignored)) $(pcf_ignored_file): echo "$(PRIVATE_IGNORED)" | tr " " "\n" >$@ $(call declare-0p-target,$(pcf_ignored_file)) $(call dist-for-goals,droidcore-unbundled,$(pcf_ignored_file):logs/$(notdir $(pcf_ignored_file))) pcf_ignored_file := product_copy_files_ignored := unique_product_copy_files_pairs := unique_product_copy_files_destinations := # Returns a list of EXTRA_INSTALL_ZIPS trios whose primary file is contained within $(1) # The trios will contain the primary installed file : the directory to unzip the zip to : the zip define relevant-extra-install-zips $(strip $(foreach p,$(EXTRA_INSTALL_ZIPS), \ $(if $(filter $(call word-colon,1,$(p)),$(1)), \ $(p)))) endef # Writes a text file that contains all of the files that will be inside a partition. # All the file paths will be relative to the partition's staging directory. # It will also take into account files inside zips listed in EXTRA_INSTALL_ZIPS. # # Arguments: # $(1): Output file # $(2): The partition's staging directory # $(3): Files to include in the partition define write-partition-file-list $(1): PRIVATE_FILES := $(subst $(2)/,,$(filter $(2)/%,$(3))) $(1): PRIVATE_EXTRA_INSTALL_ZIPS := $(call relevant-extra-install-zips,$(filter $(2)/%,$(3))) $(1): $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(foreach p,$(call relevant-extra-install-zips,$(filter $(2)/%,$(3))),$(call word-colon,3,$(p))) @echo Writing $$@ rm -f $$@ echo -n > $$@ $$(foreach f,$$(PRIVATE_FILES),echo "$$(f)" >> $$@$$(newline)) $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $$(PRIVATE_EXTRA_INSTALL_ZIPS) >> $$@ endef # ----------------------------------------------------------------- # Returns the max allowed size for an image suitable for hash verification # (e.g., boot.img, recovery.img, etc). # The value 69632 derives from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in $(AVBTOOL). # $(1): partition size to flash the image define get-hash-image-max-size $(if $(1), \ $(if $(filter true,$(BOARD_AVB_ENABLE)), \ $(eval _hash_meta_size := 69632), \ $(eval _hash_meta_size := 0)) \ $(1)-$(_hash_meta_size)) endef # ----------------------------------------------------------------- # Define rules to copy headers defined in copy_headers.mk # If more than one makefile declared a header, print a warning, # then copy the last one defined. This matches the previous make # behavior. has_dup_copy_headers := $(foreach dest,$(ALL_COPIED_HEADERS), \ $(eval _srcs := $(ALL_COPIED_HEADERS.$(dest).SRC)) \ $(eval _src := $(lastword $(_srcs))) \ $(if $(call streq,$(_src),$(_srcs)),, \ $(warning Duplicate header copy: $(dest)) \ $(warning _ Using $(_src)) \ $(warning __ from $(lastword $(ALL_COPIED_HEADERS.$(dest).MAKEFILE))) \ $(eval _makefiles := $$(wordlist 1,$(call int_subtract,$(words $(ALL_COPIED_HEADERS.$(dest).MAKEFILE)),1),$$(ALL_COPIED_HEADERS.$$(dest).MAKEFILE))) \ $(foreach src,$(wordlist 1,$(call int_subtract,$(words $(_srcs)),1),$(_srcs)), \ $(warning _ Ignoring $(src)) \ $(warning __ from $(firstword $(_makefiles))) \ $(eval _makefiles := $$(wordlist 2,9999,$$(_makefiles)))) \ $(eval has_dup_copy_headers := true)) \ $(eval $(call copy-one-header,$(_src),$(dest)))) all_copied_headers: $(ALL_COPIED_HEADERS) ifdef has_dup_copy_headers has_dup_copy_headers := $(error duplicate header copies are no longer allowed. For more information about headers, see: https://android.googlesource.com/platform/build/soong/+/master/docs/best_practices.md#headers) endif $(file >$(PRODUCT_OUT)/.copied_headers_list,$(TARGET_OUT_HEADERS) $(ALL_COPIED_HEADERS)) # ----------------------------------------------------------------- # docs/index.html ifeq (,$(TARGET_BUILD_UNBUNDLED)) gen := $(OUT_DOCS)/index.html ALL_DOCS += $(gen) $(gen): frameworks/base/docs/docs-redirect-index.html @mkdir -p $(dir $@) @cp -f $< $@ endif ndk_doxygen_out := $(OUT_NDK_DOCS) ndk_headers := $(SOONG_OUT_DIR)/ndk/sysroot/usr/include ndk_docs_src_dir := frameworks/native/docs ndk_doxyfile := $(ndk_docs_src_dir)/Doxyfile ifneq ($(wildcard $(ndk_docs_src_dir)),) ndk_docs_srcs := $(addprefix $(ndk_docs_src_dir)/,\ $(call find-files-in-subdirs,$(ndk_docs_src_dir),"*",.)) $(ndk_doxygen_out)/index.html: $(ndk_docs_srcs) $(SOONG_OUT_DIR)/ndk.timestamp @mkdir -p $(ndk_doxygen_out) @echo "Generating NDK docs to $(ndk_doxygen_out)" @( cat $(ndk_doxyfile); \ echo "INPUT=$(ndk_headers)"; \ echo "HTML_OUTPUT=$(ndk_doxygen_out)" \ ) | doxygen - $(call declare-1p-target,$(ndk_doxygen_out)/index.html,) # Note: Not a part of the docs target because we don't have doxygen available. # You can run this target locally if you have doxygen installed. ndk-docs: $(ndk_doxygen_out)/index.html .PHONY: ndk-docs endif INSTALLED_RECOVERYIMAGE_TARGET := # Build recovery image if # BUILDING_RECOVERY_IMAGE && !BOARD_USES_RECOVERY_AS_BOOT && !BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT. # If BOARD_USES_RECOVERY_AS_BOOT is true, leave empty because INSTALLED_BOOTIMAGE_TARGET is built # with recovery resources. # If BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is true, leave empty to build recovery resources # but not the final recovery image. ifdef BUILDING_RECOVERY_IMAGE ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) ifneq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img endif endif endif # Do this early because sysprop.mk depends on BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC. ifeq (default,$(ENABLE_UFFD_GC)) BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC := $(OUT_DIR)/soong/dexpreopt/kernel_version_for_uffd_gc.txt endif # ENABLE_UFFD_GC include $(BUILD_SYSTEM)/sysprop.mk # ---------------------------------------------------------------- # ----------------------------------------------------------------- # sdk-build.prop # # There are certain things in build.prop that we don't want to # ship with the sdk; remove them. # This must be a list of entire property keys followed by # "=" characters, without any internal spaces. sdk_build_prop_remove := \ ro.build.user= \ ro.build.host= \ ro.product.brand= \ ro.product.manufacturer= \ ro.product.device= # TODO: Remove this soon-to-be obsolete property sdk_build_prop_remove += ro.build.product= INSTALLED_SDK_BUILD_PROP_TARGET := $(PRODUCT_OUT)/sdk/sdk-build.prop $(INSTALLED_SDK_BUILD_PROP_TARGET): $(INSTALLED_BUILD_PROP_TARGET) @echo SDK buildinfo: $@ @mkdir -p $(dir $@) $(hide) grep -v "$(subst $(space),\|,$(strip \ $(sdk_build_prop_remove)))" $< > $@.tmp $(hide) for x in $(strip $(sdk_build_prop_remove)); do \ echo "$$x"generic >> $@.tmp; done $(hide) mv $@.tmp $@ $(call declare-0p-target,$(INSTALLED_SDK_BUILD_PROP_TARGET)) # ----------------------------------------------------------------- # declare recovery ramdisk files ifeq ($(BUILDING_RECOVERY_IMAGE),true) INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP := $(call intermediates-dir-for,PACKAGING,recovery)/ramdisk_files-timestamp endif # ----------------------------------------------------------------- # Declare vendor ramdisk fragments INTERNAL_VENDOR_RAMDISK_FRAGMENTS := ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) ifneq (,$(filter recovery,$(BOARD_VENDOR_RAMDISK_FRAGMENTS))) $(error BOARD_VENDOR_RAMDISK_FRAGMENTS must not contain "recovery" if \ BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT is set) endif INTERNAL_VENDOR_RAMDISK_FRAGMENTS += recovery VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR := $(TARGET_RECOVERY_ROOT_OUT) VENDOR_RAMDISK_FRAGMENT.recovery.FILES := $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) BOARD_VENDOR_RAMDISK_FRAGMENT.recovery.MKBOOTIMG_ARGS += --ramdisk_type RECOVERY .KATI_READONLY := VENDOR_RAMDISK_FRAGMENT.recovery.STAGING_DIR endif # Validation check and assign default --ramdisk_type. $(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \ $(if $(and $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)), \ $(error Must not specify KERNEL_MODULE_DIRS for prebuilt vendor ramdisk fragment "$(vendor_ramdisk_fragment)": $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS))) \ $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragment-stage-$(vendor_ramdisk_fragment))) \ $(eval VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES :=) \ $(if $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ $(if $(filter --ramdisk_type,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)),, \ $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_type DLKM))) \ ) # Create the "kernel module directory" to "vendor ramdisk fragment" inverse mapping. $(foreach vendor_ramdisk_fragment,$(BOARD_VENDOR_RAMDISK_FRAGMENTS), \ $(foreach kmd,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).KERNEL_MODULE_DIRS), \ $(eval kmd_vrf := KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd)) \ $(if $($(kmd_vrf)),$(error Kernel module directory "$(kmd)" belongs to multiple vendor ramdisk fragments: "$($(kmd_vrf))" "$(vendor_ramdisk_fragment)", each kernel module directory should belong to exactly one or none vendor ramdisk fragment)) \ $(eval $(kmd_vrf) := $(vendor_ramdisk_fragment)) \ ) \ ) INTERNAL_VENDOR_RAMDISK_FRAGMENTS += $(BOARD_VENDOR_RAMDISK_FRAGMENTS) ifneq ($(BOARD_KERNEL_MODULES_16K),) INTERNAL_VENDOR_RAMDISK_FRAGMENTS += 16K endif # Strip the list in case of any whitespace. INTERNAL_VENDOR_RAMDISK_FRAGMENTS := \ $(strip $(INTERNAL_VENDOR_RAMDISK_FRAGMENTS)) # Assign --ramdisk_name for each vendor ramdisk fragment. $(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ $(if $(filter --ramdisk_name,$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)), \ $(error Must not specify --ramdisk_name for vendor ramdisk fragment: $(vendor_ramdisk_fragment))) \ $(eval BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS += --ramdisk_name $(vendor_ramdisk_fragment)) \ $(eval .KATI_READONLY := BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) \ ) # ----------------------------------------------------------------- # kernel modules # Depmod requires a well-formed kernel version so 0.0 is used as a placeholder. DEPMOD_STAGING_SUBDIR :=$= lib/modules/0.0 define copy-and-strip-kernel-module $(2): $(1) $(LLVM_STRIP) -o $(2) --strip-debug $(1) endef # $(1): modules list # $(2): output dir # $(3): mount point # $(4): staging dir # $(5): module load list # $(6): module load list filename # $(7): module archive # $(8): staging dir for stripped modules # $(9): module directory name # $(10): extra modules that might be dependency of modules in this partition, but should not be copied to output dir # $(11): mount point for extra modules # Returns a list of src:dest pairs to install the modules using copy-many-files. define build-image-kernel-modules $(if $(9), \ $(eval _dir := $(9)/), \ $(eval _dir :=)) \ $(foreach module,$(1), \ $(eval _src := $(module)) \ $(if $(8), \ $(eval _src := $(8)/$(notdir $(module))) \ $(eval $(call copy-and-strip-kernel-module,$(module),$(_src)))) \ $(_src):$(2)/lib/modules/$(_dir)$(notdir $(module))) \ $(eval $(call build-image-kernel-modules-depmod,$(1),$(3),$(4),$(5),$(6),$(7),$(2),$(9),$(10),$(11))) \ $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.dep:$(2)/lib/modules/$(_dir)modules.dep \ $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.alias:$(2)/lib/modules/$(_dir)modules.alias \ $(4)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep:$(2)/lib/modules/$(_dir)modules.softdep \ $(4)/$(DEPMOD_STAGING_SUBDIR)/$(6):$(2)/lib/modules/$(_dir)$(6) endef # $(1): modules list # $(2): mount point # $(3): staging dir # $(4): module load list # $(5): module load list filename # $(6): module archive # $(7): output dir # $(8): module directory name # $(9): extra modules which should not be copied to output dir, but might be dependency of modules in this partition # $(10): mount point for extra modules # TODO(b/144844424): If a module archive is being used, this step (which # generates obj/PACKAGING/.../modules.dep) also unzips the module archive into # the output directory. This should be moved to a module with a # LOCAL_POST_INSTALL_CMD so that if modules.dep is removed from the output dir, # the archive modules are restored along with modules.dep. define build-image-kernel-modules-depmod $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: .KATI_IMPLICIT_OUTPUTS := $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.alias $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(DEPMOD) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(strip $(1)) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MODULES := $(strip $(9)) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MOUNT_POINT := $(2) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MOUNT_POINT := $(10) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules/$(8) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_EXTRA_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(10)/lib/modules/$(8) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_STAGING_DIR := $(3) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(strip $(4)) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_FILE := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_ARCHIVE := $(6) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_OUTPUT_DIR := $(7) $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(1) $(6) @echo depmod $$(PRIVATE_STAGING_DIR) rm -rf $$(PRIVATE_STAGING_DIR) mkdir -p $$(PRIVATE_MODULE_DIR) $(if $(6),\ unzip -qoDD -d $$(PRIVATE_MODULE_DIR) $$(PRIVATE_MODULE_ARCHIVE); \ mkdir -p $$(PRIVATE_OUTPUT_DIR)/lib; \ cp -r $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules $$(PRIVATE_OUTPUT_DIR)/lib/; \ find $$(PRIVATE_MODULE_DIR) -type f -name '*.ko' | xargs basename -a > $$(PRIVATE_LOAD_FILE); \ ) $(if $(1),\ cp $$(PRIVATE_MODULES) $$(PRIVATE_MODULE_DIR)/; \ if [ -n "$$(PRIVATE_LOAD_MODULES)" ]; then basename -a $$(PRIVATE_LOAD_MODULES); fi > $$(PRIVATE_LOAD_FILE); \ ) # The ln -sf + find -delete sequence is to remove any modules in # PRIVATE_EXTRA_MODULES which have same basename as MODULES in PRIVATE_MODULES # Basically, it computes a set difference. When there is a duplicate module # present in both directories, we want modules in PRIVATE_MODULES to take # precedence. Since depmod does not provide any guarantee about ordering of # dependency resolution, we achieve this by maually removing any duplicate # modules with lower priority. $(if $(9),\ mkdir -p $$(PRIVATE_EXTRA_MODULE_DIR); \ find $$(PRIVATE_EXTRA_MODULE_DIR) -maxdepth 1 -type f -name "*.ko" -delete; \ cp $$(PRIVATE_EXTRA_MODULES) $$(PRIVATE_EXTRA_MODULE_DIR); \ ln -sf $$(PRIVATE_MODULE_DIR)/*.ko $$(PRIVATE_EXTRA_MODULE_DIR); \ find $$(PRIVATE_EXTRA_MODULE_DIR) -type l -delete; \ ) $(DEPMOD) -b $$(PRIVATE_STAGING_DIR) 0.0 # Turn paths in modules.dep into absolute paths sed -i.tmp -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $$(PRIVATE_STAGING_DIR)/$$(DEPMOD_STAGING_SUBDIR)/modules.dep touch $$(PRIVATE_LOAD_FILE) endef # $(1): staging dir # $(2): modules list # $(3): module load list # $(4): module load list filename # $(5): output dir define module-load-list-copy-paths $(eval $(call build-image-module-load-list,$(1),$(2),$(3),$(4))) \ $(1)/$(DEPMOD_STAGING_SUBDIR)/$(4):$(5)/lib/modules/$(4) endef # $(1): staging dir # $(2): modules list # $(3): module load list # $(4): module load list filename define build-image-module-load-list $(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): PRIVATE_LOAD_MODULES := $(3) $(1)/$(DEPMOD_STAGING_SUBDIR)/$(4): $(2) @echo load-list $$(@) @echo '$$(strip $$(notdir $$(PRIVATE_LOAD_MODULES)))' | tr ' ' '\n' > $$(@) endef # $(1): source options file # $(2): destination pathname # Returns a build rule that checks the syntax of and installs a kernel modules # options file. Strip and squeeze any extra space and blank lines. # For use via $(eval). define build-image-kernel-modules-options-file $(2): $(1) @echo "libmodprobe options $$(@)" $(hide) mkdir -p "$$(dir $$@)" $(hide) rm -f "$$@" $(hide) awk <"$$<" >"$$@" \ '/^#/ { print; next } \ NF == 0 { next } \ NF < 2 || $$$$1 != "options" \ { print "Invalid options line " FNR ": " $$$$0 >"/dev/stderr"; \ exit_status = 1; next } \ { $$$$1 = $$$$1; print } \ END { exit exit_status }' endef # $(1): source blocklist file # $(2): destination pathname # Returns a build rule that checks the syntax of and installs a kernel modules # blocklist file. Strip and squeeze any extra space and blank lines. # For use via $(eval). define build-image-kernel-modules-blocklist-file $(2): $(1) @echo "libmodprobe blocklist $$(@)" $(hide) mkdir -p "$$(dir $$@)" $(hide) rm -f "$$@" $(hide) awk <"$$<" >"$$@" \ '/^#/ { print; next } \ NF == 0 { next } \ NF != 2 || $$$$1 != "blocklist" \ { print "Invalid blocklist line " FNR ": " $$$$0 >"/dev/stderr"; \ exit_status = 1; next } \ { $$$$1 = $$$$1; print } \ END { exit exit_status }' endef # $(1): image name # $(2): build output directory (TARGET_OUT_VENDOR, TARGET_RECOVERY_ROOT_OUT, etc) # $(3): mount point # $(4): module load filename # $(5): stripped staging directory # $(6): kernel module directory name (top is an out of band value for no directory) # $(7): list of extra modules that might be dependency of modules in this partition # $(8): mount point for extra modules. e.g. system define build-image-kernel-modules-dir $(if $(filter top,$(6)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(6))$(eval _sep :=_))\ $(if $(5),\ $(eval _stripped_staging_dir := $(5)$(_sep)$(_kver)),\ $(eval _stripped_staging_dir :=))\ $(if $(strip $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver))$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver))),\ $(if $(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),,\ $(eval BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver) := $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)))) \ $(if $(filter false,$(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver))),\ $(eval BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver) :=),) \ $(eval _files := $(call build-image-kernel-modules,$(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)),$(2),$(3),$(call intermediates-dir-for,PACKAGING,depmod_$(1)$(_sep)$(_kver)),$(BOARD_$(1)_KERNEL_MODULES_LOAD$(_sep)$(_kver)),$(4),$(BOARD_$(1)_KERNEL_MODULES_ARCHIVE$(_sep)$(_kver)),$(_stripped_staging_dir),$(_kver),$(7),$(8))) \ $(call copy-many-files,$(_files)) \ $(eval _modules := $(BOARD_$(1)_KERNEL_MODULES$(_sep)$(_kver)) ANDROID-GEN ANDROID-GEN ANDROID-GEN ANDROID-GEN) \ $(eval KERNEL_MODULE_COPY_FILES += $(join $(addsuffix :,$(_modules)),$(_files)))) \ $(if $(_kver), \ $(eval _dir := $(_kver)/), \ $(eval _dir :=)) \ $(if $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \ $(eval $(call build-image-kernel-modules-options-file, \ $(BOARD_$(1)_KERNEL_MODULES_OPTIONS_FILE$(_sep)$(_kver)), \ $(2)/lib/modules/$(_dir)modules.options)) \ $(2)/lib/modules/$(_dir)modules.options) \ $(if $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \ $(eval $(call build-image-kernel-modules-blocklist-file, \ $(BOARD_$(1)_KERNEL_MODULES_BLOCKLIST_FILE$(_sep)$(_kver)), \ $(2)/lib/modules/$(_dir)modules.blocklist)) \ $(eval ALL_KERNEL_MODULES_BLOCKLIST += $(2)/lib/modules/$(_dir)modules.blocklist) \ $(2)/lib/modules/$(_dir)modules.blocklist) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-recovery-as-boot-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,ramdisk_module_list$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load,$(TARGET_RECOVERY_ROOT_OUT)))) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-vendor-ramdisk-recovery-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_RAMDISK_OUT)))) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-vendor-kernel-ramdisk-recovery-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_VENDOR_KERNEL_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_kernel_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT)))) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-vendor-charger-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_OUT_VENDOR)))) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-vendor-ramdisk-charger-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_VENDOR_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_VENDOR_RAMDISK_OUT)))) endef # $(1): kernel module directory name (top is an out of band value for no directory) define build-vendor-kernel-ramdisk-charger-load $(if $(filter top,$(1)),\ $(eval _kver :=)$(eval _sep :=),\ $(eval _kver := $(1))$(eval _sep :=_))\ $(if $(BOARD_VENDOR_KERNEL_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\ $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_kernel_ramdisk_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_KERNEL_RAMDISK_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT)))) endef ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true) # If there is no vendor boot partition, store vendor ramdisk kernel modules in the # boot ramdisk. BOARD_GENERIC_RAMDISK_KERNEL_MODULES += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES) BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD += $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD) endif ifeq ($(BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD),) BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES) endif ifneq ($(strip $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES)),) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true) BOARD_RECOVERY_KERNEL_MODULES += $(BOARD_GENERIC_RAMDISK_KERNEL_MODULES) endif endif ifneq ($(BOARD_DO_NOT_STRIP_GENERIC_RAMDISK_MODULES),true) GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_generic_ramdisk_kernel_stripped) else GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR := endif ifneq ($(BOARD_DO_NOT_STRIP_RECOVERY_MODULES),true) RECOVERY_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_recovery_stripped) else RECOVERY_STRIPPED_MODULE_STAGING_DIR := endif ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_MODULES),true) VENDOR_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_stripped) else VENDOR_STRIPPED_MODULE_STAGING_DIR := endif ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES),true) VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_ramdisk_stripped) else VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR := endif ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_KERNEL_RAMDISK_MODULES),true) VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_kernel_ramdisk_stripped) else VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR := endif BOARD_KERNEL_MODULE_DIRS += top # Default to not generating modules.load for kernel modules on system # side. We should only load these modules if they are depended by vendor # side modules. ifeq ($(BOARD_SYSTEM_KERNEL_MODULES_LOAD),) BOARD_SYSTEM_KERNEL_MODULES_LOAD := false endif $(foreach kmd,$(BOARD_KERNEL_MODULE_DIRS), \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,$(RECOVERY_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ $(eval vendor_ramdisk_fragment := $(KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd))) \ $(if $(vendor_ramdisk_fragment), \ $(eval output_dir := $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR)) \ $(eval result_var := VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).FILES) \ $(eval ### else ###), \ $(eval output_dir := $(TARGET_VENDOR_RAMDISK_OUT)) \ $(eval result_var := ALL_DEFAULT_INSTALLED_MODULES)) \ $(eval $(result_var) += $(call build-image-kernel-modules-dir,VENDOR_RAMDISK,$(output_dir),,modules.load,$(VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR_KERNEL_RAMDISK,$(TARGET_VENDOR_KERNEL_RAMDISK_OUT),,modules.load,$(VENDOR_KERNEL_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-recovery-load,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-kernel-ramdisk-recovery-load,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR,$(if $(filter true,$(BOARD_USES_VENDOR_DLKMIMAGE)),$(TARGET_OUT_VENDOR_DLKM),$(TARGET_OUT_VENDOR)),vendor,modules.load,$(VENDOR_STRIPPED_MODULE_STAGING_DIR),$(kmd),$(BOARD_SYSTEM_KERNEL_MODULES),system)) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-charger-load,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-charger-load,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-kernel-ramdisk-charger-load,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,ODM,$(if $(filter true,$(BOARD_USES_ODM_DLKMIMAGE)),$(TARGET_OUT_ODM_DLKM),$(TARGET_OUT_ODM)),odm,modules.load,,$(kmd))) \ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,SYSTEM,$(if $(filter true,$(BOARD_USES_SYSTEM_DLKMIMAGE)),$(TARGET_OUT_SYSTEM_DLKM),$(TARGET_OUT)),system,modules.load,,$(kmd))) \ $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-recovery-as-boot-load,$(kmd))),\ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,$(GENERIC_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(kmd))))) ifeq ($(BOARD_SYSTEM_KERNEL_MODULES),) ifneq ($(BOARD_SYSTEM_DLKM_SRC),) ifneq ($(wildcard $(BOARD_SYSTEM_DLKM_SRC)/*),) SYSTEM_KERNEL_MODULES := $(shell find $(BOARD_SYSTEM_DLKM_SRC) -type f) SRC_SYSTEM_KERNEL_MODULES := $(SYSTEM_KERNEL_MODULES) DST_SYSTEM_KERNEL_MODULES := $(patsubst $(BOARD_SYSTEM_DLKM_SRC)/%,:$(TARGET_OUT_SYSTEM_DLKM)/%,$(SRC_SYSTEM_KERNEL_MODULES)) SYSTEM_KERNEL_MODULE_COPY_PAIRS := $(join $(SRC_SYSTEM_KERNEL_MODULES),$(DST_SYSTEM_KERNEL_MODULES)) ALL_DEFAULT_INSTALLED_MODULES += $(call copy-many-files,$(SYSTEM_KERNEL_MODULE_COPY_PAIRS)) endif endif endif # ----------------------------------------------------------------- # Cert-to-package mapping. Used by the post-build signing tools. # Use a macro to add newline to each echo command # $1 stem name of the package # $2 certificate # $3 private key # $4 compressed # $5 partition tag # $6 output file define _apkcerts_write_line $(hide) echo 'name="$(1).apk" certificate="$2" private_key="$3"$(if $(4), compressed="$4")$(if $(5), partition="$5")' >> $6 endef # ----------------------------------------------------------------- # Merge an individual apkcerts output into the final apkcerts.txt output. # Use a macro to make it compatible with _apkcerts_write_line # $1 apkcerts file to be merged # $2 output file define _apkcerts_merge $(hide) cat $1 >> $2 endef name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif name := $(name)-apkcerts intermediates := \ $(call intermediates-dir-for,PACKAGING,apkcerts) APKCERTS_FILE := $(intermediates)/$(name).txt ifeq ($(RELEASE_APKCERTS_INSTALL_ONLY), true) all_apkcerts_packages := $(filter $(call product-installed-modules,$(INTERNAL_PRODUCT)),$(PACKAGES)) else all_apkcerts_packages := $(PACKAGES) endif all_apkcerts_files := $(sort $(foreach p,$(all_apkcerts_packages),$(PACKAGES.$(p).APKCERTS_FILE))) $(APKCERTS_FILE): $(all_apkcerts_files) # We don't need to really build all the modules. # TODO: rebuild APKCERTS_FILE if any app change its cert. $(APKCERTS_FILE): @echo APK certs list: $@ @mkdir -p $(dir $@) @rm -f $@ $(foreach p,$(sort $(all_apkcerts_packages)),\ $(if $(PACKAGES.$(p).APKCERTS_FILE),\ $(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\ $(if $(PACKAGES.$(p).EXTERNAL_KEY),\ $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),EXTERNAL,,$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\ $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@)))) $(if $(filter true,$(PRODUCT_FSVERITY_GENERATE_METADATA)),\ $(call _apkcerts_write_line,BuildManifest,$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system,$@) \ $(if $(filter true,$(BUILDING_SYSTEM_EXT_IMAGE)),\ $(call _apkcerts_write_line,BuildManifestSystemExt,$(FSVERITY_APK_KEY_PATH).x509.pem,$(FSVERITY_APK_KEY_PATH).pk8,,system_ext,$@))) # In case value of PACKAGES is empty. $(hide) touch $@ && sort -u -o $@ $@ $(call declare-0p-target,$(APKCERTS_FILE)) .PHONY: apkcerts-list apkcerts-list: $(APKCERTS_FILE) intermediates := $(call intermediates-dir-for,PACKAGING,apexkeys) APEX_KEYS_FILE := $(intermediates)/apexkeys.txt all_apex_keys_files := $(sort $(foreach m,$(call product-installed-modules,$(INTERNAL_PRODUCT)),$(ALL_MODULES.$(m).APEX_KEYS_FILE))) $(APEX_KEYS_FILE): $(all_apex_keys_files) @mkdir -p $(dir $@) @rm -f $@ $(hide) touch $@ $(hide) $(foreach file,$^,cat $(file) >> $@ $(newline)) all_apex_keys_files := $(call declare-0p-target,$(APEX_KEYS_FILE)) .PHONY: apexkeys.txt apexkeys.txt: $(APEX_KEYS_FILE) ifneq (,$(TARGET_BUILD_APPS)) $(call dist-for-goals, apps_only, $(APKCERTS_FILE):apkcerts.txt) $(call dist-for-goals, apps_only, $(APEX_KEYS_FILE):apexkeys.txt) endif # ----------------------------------------------------------------- # build /product/etc/security/avb/system_other.avbpubkey if needed ifdef BUILDING_SYSTEM_OTHER_IMAGE ifeq ($(BOARD_AVB_ENABLE),true) INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET := $(TARGET_OUT_PRODUCT_ETC)/security/avb/system_other.avbpubkey ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET) endif # BOARD_AVB_ENABLE endif # BUILDING_SYSTEM_OTHER_IMAGE # ----------------------------------------------------------------- # Modules ready to be converted to Soong, ordered by how many # modules depend on them. SOONG_CONV := $(sort $(SOONG_CONV)) SOONG_CONV_DATA := $(call intermediates-dir-for,PACKAGING,soong_conversion)/soong_conv_data $(SOONG_CONV_DATA): @rm -f $@ @touch $@ # This file must be present even if SOONG_CONV is empty. @$(foreach s,$(SOONG_CONV),echo "$(s),$(SOONG_CONV.$(s).TYPE),$(sort $(SOONG_CONV.$(s).PROBLEMS)),$(sort $(filter-out $(SOONG_ALREADY_CONV),$(SOONG_CONV.$(s).DEPS))),$(sort $(SOONG_CONV.$(s).MAKEFILES)),$(sort $(SOONG_CONV.$(s).INSTALLED))" >>$@;) $(call declare-1p-target,$(SOONG_CONV_DATA),build) SOONG_TO_CONVERT_SCRIPT := build/make/tools/soong_to_convert.py SOONG_TO_CONVERT := $(PRODUCT_OUT)/soong_to_convert.txt $(SOONG_TO_CONVERT): $(SOONG_CONV_DATA) $(SOONG_TO_CONVERT_SCRIPT) @rm -f $@ $(hide) $(SOONG_TO_CONVERT_SCRIPT) $< >$@ $(call declare-1p-target,$(SOONG_TO_CONVERT),build) $(call dist-for-goals,droidcore-unbundled,$(SOONG_TO_CONVERT)) MK2BP_CATALOG_SCRIPT := build/make/tools/mk2bp_catalog.py PRODUCT_PACKAGES_TXT := $(PRODUCT_OUT)/product_packages.txt MK2BP_REMAINING_HTML := $(PRODUCT_OUT)/mk2bp_remaining.html $(MK2BP_REMAINING_HTML): PRIVATE_CODE_SEARCH_BASE_URL := "https://cs.android.com/android/platform/superproject/+/master:" $(MK2BP_REMAINING_HTML): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT) @rm -f $@ $(hide) $(MK2BP_CATALOG_SCRIPT) \ --device=$(TARGET_DEVICE) \ --product-packages=$(PRODUCT_PACKAGES_TXT) \ --title="Remaining Android.mk files for $(TARGET_DEVICE)-$(TARGET_BUILD_VARIANT)" \ --codesearch=$(PRIVATE_CODE_SEARCH_BASE_URL) \ --out-dir="$(OUT_DIR)" \ --mode=html \ > $@ $(call declare-1p-target,$(MK2BP_REMAINING_HTML),build) $(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_HTML)) MK2BP_REMAINING_CSV := $(PRODUCT_OUT)/mk2bp_remaining.csv $(MK2BP_REMAINING_CSV): $(SOONG_CONV_DATA) $(MK2BP_CATALOG_SCRIPT) $(PRODUCT_PACKAGES_TXT) @rm -f $@ $(hide) $(MK2BP_CATALOG_SCRIPT) \ --device=$(TARGET_DEVICE) \ --product-packages=$(PRODUCT_PACKAGES_TXT) \ --out-dir="$(OUT_DIR)" \ --mode=csv \ > $@ $(call declare-1p-target,$(MK2BP_REMAINING_CSV)) $(call dist-for-goals,droidcore-unbundled,$(MK2BP_REMAINING_CSV)) .PHONY: mk2bp_remaining mk2bp_remaining: $(MK2BP_REMAINING_HTML) $(MK2BP_REMAINING_CSV) # ----------------------------------------------------------------- # Modules use -Wno-error, or added default -Wall -Werror WALL_WERROR := $(PRODUCT_OUT)/wall_werror.txt $(WALL_WERROR): @rm -f $@ echo "# Modules using -Wno-error" >> $@ for m in $(sort $(SOONG_MODULES_USING_WNO_ERROR) $(MODULES_USING_WNO_ERROR)); do echo $$m >> $@; done echo "# Modules that allow warnings" >> $@ for m in $(sort $(SOONG_MODULES_WARNINGS_ALLOWED) $(MODULES_WARNINGS_ALLOWED)); do echo $$m >> $@; done $(call declare-0p-target,$(WALL_WERROR)) $(call dist-for-goals,droidcore-unbundled,$(WALL_WERROR)) CERTIFICATE_VIOLATION_MODULES_FILENAME := $(PRODUCT_OUT)/certificate_violation_modules.txt $(CERTIFICATE_VIOLATION_MODULES_FILENAME): rm -f $@ $(foreach m,$(sort $(CERTIFICATE_VIOLATION_MODULES)), echo $(m) >> $@;) $(call declare-0p-target,$(CERTIFICATE_VIOLATION_MODULES_FILENAME)) $(call dist-for-goals,droidcore,$(CERTIFICATE_VIOLATION_MODULES_FILENAME)) # ----------------------------------------------------------------- # The dev key is used to sign this package, and as the key required # for future OTA packages installed by this system. Actual product # deliverables will be re-signed by hand. We expect this file to # exist with the suffixes ".x509.pem" and ".pk8". DEFAULT_KEY_CERT_PAIR := $(strip $(DEFAULT_SYSTEM_DEV_CERTIFICATE)) # Rules that need to be present for the all targets, even # if they don't do anything. .PHONY: systemimage systemimage: # ----------------------------------------------------------------- event_log_tags_file := $(TARGET_OUT)/etc/event-log-tags # Include tags from all packages that we know about all_event_log_tags_src := \ $(sort $(foreach m, $(ALL_MODULES), $(ALL_MODULES.$(m).EVENT_LOG_TAGS))) # Include tags from all packages included in this product, plus all # tags that are part of the system (ie, not in a vendor/ or device/ # directory). event_log_tags_src := \ $(sort $(foreach m,\ $(call resolve-bitness-for-modules,TARGET,$(PRODUCT_PACKAGES)) \ $(call module-names-for-tag-list,user), \ $(ALL_MODULES.$(m).EVENT_LOG_TAGS)) \ $(filter-out vendor/% device/% out/%,$(all_event_log_tags_src))) $(event_log_tags_file): PRIVATE_SRC_FILES := $(event_log_tags_src) $(event_log_tags_file): $(event_log_tags_src) $(MERGETAGS) $(hide) mkdir -p $(dir $@) $(hide) $(MERGETAGS) -o $@ $(PRIVATE_SRC_FILES) $(eval $(call declare-0p-target,$(event_log_tags_file))) .PHONY: event-log-tags event-log-tags: $(event_log_tags_file) ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file) # Initialize INSTALLED_FILES_OUTSIDE_IMAGES with the list of all device files, # files installed in images will be filtered out later. INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out \ $(PRODUCT_OUT)/apex/% \ $(PRODUCT_OUT)/fake_packages/% \ $(PRODUCT_OUT)/testcases/%, \ $(filter $(PRODUCT_OUT)/%,$(ALL_DEFAULT_INSTALLED_MODULES))) # ################################################################# # Targets for boot/OS images # ################################################################# ifneq ($(strip $(TARGET_NO_BOOTLOADER)),true) INSTALLED_BOOTLOADER_MODULE := $(PRODUCT_OUT)/bootloader ifdef BOARD_PREBUILT_BOOTLOADER $(eval $(call copy-one-file,$(BOARD_PREBUILT_BOOTLOADER),$(INSTALLED_BOOTLOADER_MODULE))) $(call dist-for-goals,dist_files,$(INSTALLED_BOOTLOADER_MODULE)) endif # BOARD_PREBUILT_BOOTLOADER ifeq ($(strip $(TARGET_BOOTLOADER_IS_2ND)),true) INSTALLED_2NDBOOTLOADER_TARGET := $(PRODUCT_OUT)/2ndbootloader else INSTALLED_2NDBOOTLOADER_TARGET := endif else INSTALLED_BOOTLOADER_MODULE := INSTALLED_2NDBOOTLOADER_TARGET := endif # TARGET_NO_BOOTLOADER ifneq ($(strip $(TARGET_NO_KERNEL)),true) ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) INSTALLED_KERNEL_TARGET := $(foreach k,$(BOARD_KERNEL_BINARIES), \ $(PRODUCT_OUT)/$(k)) else INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel endif else INSTALLED_KERNEL_TARGET := endif # ----------------------------------------------------------------- # the root dir INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_ROOT_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) INTERNAL_ROOT_FILES := $(filter $(TARGET_ROOT_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_ROOT := $(PRODUCT_OUT)/installed-files-root.txt INSTALLED_FILES_JSON_ROOT := $(INSTALLED_FILES_FILE_ROOT:.txt=.json) $(INSTALLED_FILES_FILE_ROOT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ROOT) $(INSTALLED_FILES_FILE_ROOT) : $(INTERNAL_ROOT_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(TARGET_ROOT_OUT) mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_ROOT)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_ROOT)) #------------------------------------------------------------------ # dtb ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG INSTALLED_DTBIMAGE_TARGET := $(PRODUCT_OUT)/dtb.img ifdef BOARD_PREBUILT_DTBIMAGE_DIR $(INSTALLED_DTBIMAGE_TARGET) : $(sort $(wildcard $(BOARD_PREBUILT_DTBIMAGE_DIR)/*.dtb)) cat $^ > $@ endif endif # ----------------------------------------------------------------- # dtbo image ifdef BOARD_PREBUILT_DTBOIMAGE INSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH) cp $(BOARD_PREBUILT_DTBOIMAGE) $@ chmod +w $@ $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \ --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \ $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS) $(call declare-1p-container,$(INSTALLED_DTBOIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_TARGET),$(BOARD_PREBUILT_DTBOIMAGE),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DTBOIMAGE_TARGET) else $(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) cp $(BOARD_PREBUILT_DTBOIMAGE) $@ endif endif # BOARD_PREBUILT_DTBOIMAGE # ----------------------------------------------------------------- # ----------------------------------------------------------------- # the ramdisk INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_RAMDISK_IMAGE INTERNAL_RAMDISK_FILES := $(filter $(TARGET_RAMDISK_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk.txt INSTALLED_FILES_JSON_RAMDISK := $(INSTALLED_FILES_FILE_RAMDISK:.txt=.json) $(INSTALLED_FILES_FILE_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RAMDISK) $(INSTALLED_FILES_FILE_RAMDISK) : $(INTERNAL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(TARGET_RAMDISK_OUT) mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_RAMDISK_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RAMDISK))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RAMDISK))) BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img ifeq ($(BOARD_RAMDISK_USE_LZ4),true) # -l enables the legacy format used by the Linux kernel COMPRESSION_COMMAND_DEPS := $(LZ4) COMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed RAMDISK_EXT := .lz4 else COMPRESSION_COMMAND_DEPS := $(GZIP) COMPRESSION_COMMAND := $(GZIP) RAMDISK_EXT := .gz endif ifneq ($(BOARD_KERNEL_MODULES_16K),) TARGET_OUT_RAMDISK_16K := $(PRODUCT_OUT)/ramdisk_16k BUILT_RAMDISK_16K_TARGET := $(PRODUCT_OUT)/ramdisk_16k.img RAMDISK_16K_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_ramdisk_16k) ifneq ($(BOARD_SYSTEM_KERNEL_MODULES),) SYSTEM_DLKM_MODULE_PATTERNS := $(foreach path,$(BOARD_SYSTEM_KERNEL_MODULES),%/$(notdir $(path))) endif # BOARD_KERNEL_MODULES_16K might contain duplicate modules under different path. # for example, foo/bar/wifi.ko and foo/wifi.ko . To avoid build issues, de-dup # module list on basename first. BOARD_KERNEL_MODULES_16K := $(foreach \ pattern,\ $(sort $(foreach \ path,\ $(BOARD_KERNEL_MODULES_16K),\ %/$(notdir $(path)))\ ),\ $(firstword $(filter $(pattern),$(BOARD_KERNEL_MODULES_16K))) \ ) # For non-GKI modules, strip them before install. As debug symbols take up # significant space. $(foreach \ file,\ $(filter-out $(SYSTEM_DLKM_MODULE_PATTERNS),$(BOARD_KERNEL_MODULES_16K)),\ $(eval \ $(call copy-and-strip-kernel-module,\ $(file),\ $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)) \ ) \ ) \ ) # For GKI modules, copy as-is without stripping, because stripping would # remove the signature of kernel modules, and GKI modules must be signed # for kernel to load them. $(foreach \ file,\ $(filter $(SYSTEM_DLKM_MODULE_PATTERNS),$(BOARD_KERNEL_MODULES_16K)),\ $(eval \ $(call copy-one-file,\ $(file),\ $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)) \ ) \ ) \ ) BOARD_VENDOR_RAMDISK_FRAGMENT.16K.PREBUILT := $(BUILT_RAMDISK_16K_TARGET) ifndef BOARD_KERNEL_MODULES_LOAD_16K BOARD_KERNEL_MODULES_LOAD_16K := $(BOARD_KERNEL_MODULES_16K) endif $(BUILT_RAMDISK_16K_TARGET): $(DEPMOD) $(MKBOOTFS) $(EXTRACT_KERNEL) $(COMPRESSION_COMMAND_DEPS) $(BUILT_RAMDISK_16K_TARGET): $(foreach file,$(BOARD_KERNEL_MODULES_16K),$(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file))) $(DEPMOD) -b $(RAMDISK_16K_STAGING_DIR) 0.0 rm -f $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/modules.load for MODULE in $(BOARD_KERNEL_MODULES_LOAD_16K); do \ basename $$MODULE >> $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/modules.load ; \ done; rm -rf $(TARGET_OUT_RAMDISK_16K)/lib/modules mkdir -p $(TARGET_OUT_RAMDISK_16K)/lib/modules KERNEL_RELEASE=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-release` ;\ IS_16K_KERNEL=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-config` ;\ if [[ "$$IS_16K_KERNEL" == *"CONFIG_ARM64_16K_PAGES=y"* ]]; then SUFFIX=_16k; fi ;\ cp -r $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0 $(TARGET_OUT_RAMDISK_16K)/lib/modules/$$KERNEL_RELEASE$$SUFFIX $(MKBOOTFS) $(TARGET_OUT_RAMDISK_16K) | $(COMPRESSION_COMMAND) > $@ # Builds a ramdisk using modules defined in BOARD_KERNEL_MODULES_16K ramdisk_16k: $(BUILT_RAMDISK_16K_TARGET) .PHONY: ramdisk_16k endif # ----------------------------------------------------------------- # 16KB dtbo image ifdef BOARD_PREBUILT_DTBOIMAGE_16KB INSTALLED_DTBOIMAGE_16KB_TARGET := $(PRODUCT_OUT)/dtbo_16k.img ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH) cp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@ chmod +w $@ $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \ --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \ $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS) $(call declare-1p-container,$(INSTALLED_DTBOIMAGE_16KB_TARGET),) $(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_16KB_TARGET),$(BOARD_PREBUILT_DTBOIMAGE_16KB),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_DTBOIMAGE_16KB_TARGET) else $(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB) cp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@ endif endif # BOARD_PREBUILT_DTBOIMAGE_16KB ramdisk_intermediates :=$= $(call intermediates-dir-for,PACKAGING,ramdisk) $(eval $(call write-partition-file-list,$(ramdisk_intermediates)/file_list.txt,$(TARGET_RAMDISK_OUT),$(INTERNAL_RAMDISK_FILES))) # The value of RAMDISK_NODE_LIST is defined in system/core/rootdir/Android.bp. # This file contains /dev nodes description added to the generic ramdisk # We just build this directly to the install location. INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET) $(INSTALLED_RAMDISK_TARGET): PRIVATE_DIRS := debug_ramdisk dev metadata mnt proc second_stage_resources sys $(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(RAMDISK_NODE_LIST) $(INTERNAL_RAMDISK_FILES) $(INSTALLED_FILES_FILE_RAMDISK) | $(COMPRESSION_COMMAND_DEPS) $(call pretty,"Target ramdisk: $@") $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(PRIVATE_DIRS)) ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) $(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(PRIVATE_DIRS)) endif $(hide) $(MKBOOTFS) -n $(RAMDISK_NODE_LIST) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@ $(call declare-1p-container,$(INSTALLED_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_RAMDISK_TARGET),$(INTERNAL_RAMDISK_FILE),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_RAMDISK_TARGET) .PHONY: ramdisk-nodeps ramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) @echo "make $@: ignoring dependencies" $(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $(INSTALLED_RAMDISK_TARGET) endif # BUILDING_RAMDISK_IMAGE # ----------------------------------------------------------------- # the boot image, which is a collection of other images. # This is defined here since we may be building recovery as boot # below and only want to define this once ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) BUILT_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot,$(BOARD_KERNEL_BINARIES)), $(PRODUCT_OUT)/$(k).img) else BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img endif INTERNAL_PREBUILT_BOOTIMAGE := my_installed_prebuilt_gki_apex := $(strip $(foreach package,$(PRODUCT_PACKAGES),$(if $(ALL_MODULES.$(package).EXTRACTED_BOOT_IMAGE),$(package)))) ifdef my_installed_prebuilt_gki_apex ifneq (1,$(words $(my_installed_prebuilt_gki_apex))) # len(my_installed_prebuilt_gki_apex) > 1 $(error More than one prebuilt GKI APEXes are installed: $(my_installed_prebuilt_gki_apex)) endif # len(my_installed_prebuilt_gki_apex) > 1 ifdef BOARD_PREBUILT_BOOTIMAGE $(error Must not define BOARD_PREBUILT_BOOTIMAGE because a prebuilt GKI APEX is installed: $(my_installed_prebuilt_gki_apex)) endif # BOARD_PREBUILT_BOOTIMAGE defined my_apex_extracted_boot_image := $(ALL_MODULES.$(my_installed_prebuilt_gki_apex).EXTRACTED_BOOT_IMAGE) INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img $(eval $(call copy-one-file,$(my_apex_extracted_boot_image),$(INSTALLED_BOOTIMAGE_TARGET))) $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) INTERNAL_PREBUILT_BOOTIMAGE := $(my_apex_extracted_boot_image) else # my_installed_prebuilt_gki_apex not defined # $1: boot image target # returns the kernel used to make the bootimage define bootimage-to-kernel $(if $(BOARD_KERNEL_BINARIES),\ $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $(1)))),\ $(INSTALLED_KERNEL_TARGET)) endef ifdef BOARD_BOOTIMAGE_PARTITION_SIZE BOARD_KERNEL_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE) endif # $1: boot image file name # $2: boot image variant (boot, boot-debug, boot-test-harness) define get-bootimage-partition-size $(BOARD_$(call to-upper,$(subst .img,,$(subst $(2),kernel,$(notdir $(1)))))_BOOTIMAGE_PARTITION_SIZE) endef # $1: partition size define get-partition-size-argument $(if $(1),--partition_size $(1),--dynamic_partition_size) endef # $1: output boot image target # $2: input path to kernel binary define build_boot_from_kernel_avb_enabled $(eval kernel := $(2)) $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))) $(AVBTOOL) add_hash_footer \ --image $(1) \ $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \ --salt `sha256sum "$(kernel)" | cut -d " " -f 1` \ --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) endef ifndef BOARD_PREBUILT_BOOTIMAGE ifneq ($(strip $(TARGET_NO_KERNEL)),true) INTERNAL_BOOTIMAGE_ARGS := \ $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) ifneq ($(BUILDING_INIT_BOOT_IMAGE),true) INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET) endif ifndef BUILDING_VENDOR_BOOT_IMAGE ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG INTERNAL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) endif endif INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS)) # kernel cmdline/base/pagesize in boot. # - If using GKI, use GENERIC_KERNEL_CMDLINE. Remove kernel base and pagesize because they are # device-specific. # - If not using GKI: # - If building vendor_boot, INTERNAL_KERNEL_CMDLINE, base and pagesize goes in vendor_boot. # - Otherwise, put them in boot. ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) ifdef GENERIC_KERNEL_CMDLINE INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(GENERIC_KERNEL_CMDLINE)" endif else ifndef BUILDING_VENDOR_BOOT_IMAGE # && BOARD_USES_GENERIC_KERNEL_IMAGE != true ifdef INTERNAL_KERNEL_CMDLINE INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)" endif ifdef BOARD_KERNEL_BASE INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) endif ifdef BOARD_KERNEL_PAGESIZE INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) endif endif # BUILDING_VENDOR_BOOT_IMAGE == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true INTERNAL_MKBOOTIMG_VERSION_ARGS := \ --os_version $(PLATFORM_VERSION_LAST_STABLE) \ --os_patch_level $(PLATFORM_SECURITY_PATCH) # Define these only if we are building boot ifdef BUILDING_BOOT_IMAGE INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET) ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true) $(error TARGET_BOOTIMAGE_USE_EXT2 is not supported anymore) endif # TARGET_BOOTIMAGE_USE_EXT2 $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b))))) ifeq (true,$(BOARD_AVB_ENABLE)) # $1: boot image target define build_boot_board_avb_enabled $(eval kernel := $(call bootimage-to-kernel,$(1))) $(call build_boot_from_kernel_avb_enabled,$(1),$(kernel)) endef $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(call pretty,"Target boot image: $@") $(call build_boot_board_avb_enabled,$@) $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) $(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) .PHONY: bootimage-nodeps bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) @echo "make $@: ignoring dependencies" $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_board_avb_enabled,$(b))) else # BOARD_AVB_ENABLE != true # $1: boot image target define build_boot_novboot $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) $(call assert-max-image-size,$1,$(call get-bootimage-partition-size,$(1),boot)) endef $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(call pretty,"Target boot image: $@") $(call build_boot_novboot,$@) $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) $(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_BOOTIMAGE_FILES),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) .PHONY: bootimage-nodeps bootimage-nodeps: $(MKBOOTIMG) @echo "make $@: ignoring dependencies" $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_novboot,$(b))) endif # BOARD_AVB_ENABLE endif # BUILDING_BOOT_IMAGE else # TARGET_NO_KERNEL == "true" INSTALLED_BOOTIMAGE_TARGET := endif # TARGET_NO_KERNEL else # BOARD_PREBUILT_BOOTIMAGE defined INTERNAL_PREBUILT_BOOTIMAGE := $(BOARD_PREBUILT_BOOTIMAGE) INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_BOOTIMAGE_TARGET): PRIVATE_WORKING_DIR := $(call intermediates-dir-for,PACKAGING,prebuilt_bootimg) $(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(UNPACK_BOOTIMG) cp $(INTERNAL_PREBUILT_BOOTIMAGE) $@ $(UNPACK_BOOTIMG) --boot_img $(INTERNAL_PREBUILT_BOOTIMAGE) --out $(PRIVATE_WORKING_DIR) chmod +w $@ $(AVBTOOL) add_hash_footer \ --image $@ \ --salt `sha256sum $(PRIVATE_WORKING_DIR)/kernel | cut -d " " -f 1` \ $(call get-partition-size-argument,$(BOARD_BOOTIMAGE_PARTITION_SIZE)) \ --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",bool) $(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(INTERNAL_PREBUILT_BOOTIMAGE),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_BOOTIMAGE_TARGET) else $(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_BOOTIMAGE) cp $(INTERNAL_PREBUILT_BOOTIMAGE) $@ endif # BOARD_AVB_ENABLE endif # BOARD_PREBUILT_BOOTIMAGE endif # my_installed_prebuilt_gki_apex not defined ifneq ($(BOARD_KERNEL_PATH_16K),) BUILT_KERNEL_16K_TARGET := $(PRODUCT_OUT)/kernel_16k $(eval $(call copy-one-file,$(BOARD_KERNEL_PATH_16K),$(BUILT_KERNEL_16K_TARGET))) # Copies BOARD_KERNEL_PATH_16K to output directory as is kernel_16k: $(BUILT_KERNEL_16K_TARGET) .PHONY: kernel_16k BUILT_BOOTIMAGE_16K_TARGET := $(PRODUCT_OUT)/boot_16k.img BOARD_KERNEL_16K_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE) $(BUILT_BOOTIMAGE_16K_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(BUILT_KERNEL_16K_TARGET) $(call pretty,"Target boot 16k image: $@") $(call build_boot_from_kernel_avb_enabled,$@,$(BUILT_KERNEL_16K_TARGET)) bootimage_16k: $(BUILT_BOOTIMAGE_16K_TARGET) .PHONY: bootimage_16k BUILT_BOOT_OTA_PACKAGE_16K := $(PRODUCT_OUT)/boot_ota_16k.zip $(BUILT_BOOT_OTA_PACKAGE_16K): PRIVATE_BOOTIMAGE_TARGET := $(INSTALLED_BOOTIMAGE_TARGET) $(BUILT_BOOT_OTA_PACKAGE_16K): PRIVATE_BOOTIMAGE_16KB_TARGET := $(BUILT_BOOTIMAGE_16K_TARGET) $(BUILT_BOOT_OTA_PACKAGE_16K): $(OTA_FROM_RAW_IMG) \ $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(BUILT_BOOTIMAGE_16K_TARGET) \ $(INSTALLED_DTBOIMAGE_16KB_TARGET) \ $(INSTALLED_DTBOIMAGE_TARGET) $(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \ --max_timestamp `cat $(BUILD_DATETIME_FILE)` \ --path $(HOST_OUT) \ --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\ $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\ boot$(comma)dtbo,\ boot) \ --output $@ \ $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\ $(PRIVATE_BOOTIMAGE_TARGET):$(PRIVATE_BOOTIMAGE_16KB_TARGET),\ $(PRIVATE_BOOTIMAGE_16KB_TARGET)\ )\ $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\ $(INSTALLED_DTBOIMAGE_16KB_TARGET)) boototapackage_16k: $(BUILT_BOOT_OTA_PACKAGE_16K) .PHONY: boototapackage_16k BUILT_BOOT_OTA_PACKAGE_4K := $(PRODUCT_OUT)/boot_ota_4k.zip $(BUILT_BOOT_OTA_PACKAGE_4K): $(OTA_FROM_RAW_IMG) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(BUILT_BOOTIMAGE_16K_TARGET) \ $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \ $(INSTALLED_DTBOIMAGE_TARGET) \ $(INSTALLED_DTBOIMAGE_16KB_TARGET) $(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \ --max_timestamp `cat $(BUILD_DATETIME_FILE)` \ --path $(HOST_OUT) \ --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\ $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\ boot$(comma)dtbo,\ boot) \ --output $@ \ $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\ $(BUILT_BOOTIMAGE_16K_TARGET):$(INSTALLED_BOOTIMAGE_TARGET),\ $(INSTALLED_BOOTIMAGE_TARGET)\ )\ $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\ $(INSTALLED_DTBOIMAGE_TARGET)) boototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K) .PHONY: boototapackage_4k ifeq ($(BOARD_16K_OTA_MOVE_VENDOR),true) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip)) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip)) ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip ifneq ($(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE),) # Add the modules that need to be loaded in the Second Boot Stage # to /vendor_dlkm/lib/modules/16k-mode VENDOR_DLKM_16K_MODE_DIR := lib/modules/16k-mode $(foreach module,$(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE), \ $(eval $(call copy-one-file,$(TARGET_KERNEL_DIR_16K)/$(module),\ $(TARGET_OUT_VENDOR_DLKM)/$(VENDOR_DLKM_16K_MODE_DIR)/$(module)))) ALL_DEFAULT_INSTALLED_MODULES += $(foreach module,$(BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE),\ $(TARGET_OUT_VENDOR_DLKM)/$(VENDOR_DLKM_16K_MODE_DIR)/$(module)) endif # BOARD_VENDOR_KERNEL_MODULES_2ND_STAGE_16KB_MODE not empty else $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT)/boot_otas/boot_ota_4k.zip)) $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT)/boot_otas/boot_ota_16k.zip)) ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_4k.zip ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_16k.zip endif # BOARD_16K_OTA_MOVE_VENDOR == true endif my_apex_extracted_boot_image := my_installed_prebuilt_gki_apex := # ----------------------------------------------------------------- # init boot image ifeq ($(BUILDING_INIT_BOOT_IMAGE),true) INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_RAMDISK_TARGET) INTERNAL_INIT_BOOT_IMAGE_ARGS := --ramdisk $(INSTALLED_RAMDISK_TARGET) ifdef BOARD_KERNEL_PAGESIZE INTERNAL_INIT_BOOT_IMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) endif ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH) $(call pretty,"Target init_boot image: $@") $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output "$@" $(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \ --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d " " -f 1 | tr -d '\n') \ --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS) $(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE),$(PRODUCT_OUT)/:/) else $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(call pretty,"Target init_boot image: $@") $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output $@ $(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) $(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) endif UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_INIT_BOOT_IMAGE_TARGET) else # BUILDING_INIT_BOOT_IMAGE is not true ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE INTERNAL_PREBUILT_INIT_BOOT_IMAGE := $(BOARD_PREBUILT_INIT_BOOT_IMAGE) INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH) cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@ chmod +w $@ $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) \ --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS) $(call declare-1p-container,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),$(INTERNAL_PREBUILT_INIT_BOOT_IMAGE),$(PRODUCT_OUT)/:/) else $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@ $(call declare-1p-target,$(INSTALLED_INIT_BOOT_IMAGE_TARGET),) endif # BOARD_AVB_ENABLE UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_INIT_BOOT_IMAGE_TARGET) else # BOARD_PREBUILT_INIT_BOOT_IMAGE not defined INSTALLED_INIT_BOOT_IMAGE_TARGET := endif # BOARD_PREBUILT_INIT_BOOT_IMAGE endif # BUILDING_INIT_BOOT_IMAGE is not true # ----------------------------------------------------------------- # vendor boot image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) INTERNAL_VENDOR_RAMDISK_FILES := $(filter $(TARGET_VENDOR_RAMDISK_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) INTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot)/vendor_ramdisk.cpio$(RAMDISK_EXT) vendor_ramdisk_intermediates :=$= $(call intermediates-dir-for,PACKAGING,vendor_ramdisk) $(eval $(call write-partition-file-list,$(vendor_ramdisk_intermediates)/file_list.txt,$(TARGET_VENDOR_RAMDISK_OUT),$(INTERNAL_VENDOR_RAMDISK_FILES))) # Exclude recovery files in the default vendor ramdisk if including a standalone # recovery ramdisk in vendor_boot. ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) $(INTERNAL_VENDOR_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) $(INTERNAL_VENDOR_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT) endif endif $(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@ INSTALLED_VENDOR_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk.img $(INSTALLED_VENDOR_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) @echo "Target vendor ramdisk: $@" $(copy-file-to-target) $(call declare-1p-container,$(INSTALLED_VENDOR_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_RAMDISK_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_RAMDISK_TARGET) INSTALLED_FILES_FILE_VENDOR_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk.txt INSTALLED_FILES_JSON_VENDOR_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_RAMDISK:.txt=.json) $(INSTALLED_FILES_FILE_VENDOR_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) $(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_FILES_FILE_VENDOR_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_VENDOR_RAMDISK_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_RAMDISK))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_RAMDISK))) ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) # If we have vendor_kernel_boot partition, we migrate dtb image to that image # and allow dtb in vendor_boot to be empty. INTERNAL_VENDOR_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) endif endif ifdef BOARD_KERNEL_BASE INTERNAL_VENDOR_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) endif ifdef BOARD_KERNEL_PAGESIZE INTERNAL_VENDOR_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) endif ifdef INTERNAL_KERNEL_CMDLINE INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_cmdline "$(INTERNAL_KERNEL_CMDLINE)" endif ifneq (, $(INTERNAL_BOOTCONFIG)$(INTERNAL_BOOTCONFIG_FILE)) INTERNAL_VENDOR_BOOTCONFIG_TARGET := $(PRODUCT_OUT)/vendor-bootconfig.img $(INTERNAL_VENDOR_BOOTCONFIG_TARGET): rm -f $@ $(foreach param,$(INTERNAL_BOOTCONFIG), \ printf "%s\n" $(param) >> $@;) cat $(INTERNAL_BOOTCONFIG_FILE) >> $@ INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_bootconfig $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) endif # $(1): Build target name # $(2): Staging dir to be compressed # $(3): Build dependencies define build-vendor-ramdisk-fragment-target $(1): $(3) $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) $(MKBOOTFS) -d $(TARGET_OUT) $(2) | $(COMPRESSION_COMMAND) > $$@ endef # $(1): Ramdisk name define build-vendor-ramdisk-fragment $(strip \ $(eval build_target := $(call intermediates-dir-for,PACKAGING,vendor_ramdisk_fragments)/$(1).cpio$(RAMDISK_EXT)) \ $(eval $(call build-vendor-ramdisk-fragment-target,$(build_target),$(VENDOR_RAMDISK_FRAGMENT.$(1).STAGING_DIR),$(VENDOR_RAMDISK_FRAGMENT.$(1).FILES))) \ $(build_target) \ ) endef # $(1): Ramdisk name # $(2): Prebuilt file path define build-prebuilt-vendor-ramdisk-fragment $(strip \ $(eval build_target := $(call intermediates-dir-for,PACKAGING,prebuilt_vendor_ramdisk_fragments)/$(1)) \ $(eval $(call copy-one-file,$(2),$(build_target))) \ $(build_target) \ ) endef INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS := INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS := $(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ $(eval prebuilt_vendor_ramdisk_fragment_file := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \ $(if $(prebuilt_vendor_ramdisk_fragment_file), \ $(eval vendor_ramdisk_fragment_target := $(call build-prebuilt-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment),$(prebuilt_vendor_ramdisk_fragment_file))) \ $(eval ### else ###), \ $(eval vendor_ramdisk_fragment_target := $(call build-vendor-ramdisk-fragment,$(vendor_ramdisk_fragment)))) \ $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS += $(vendor_ramdisk_fragment_target)) \ $(eval INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS += $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS) --vendor_ramdisk_fragment $(vendor_ramdisk_fragment_target)) \ ) INSTALLED_VENDOR_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot.img $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOTIMAGE_KEY_PATH) $(call pretty,"Target vendor_boot image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \ --salt $$(sha256sum $(BUILD_NUMBER_FILE) $(BUILD_DATETIME_FILE) | cut -d " " -f 1 | tr -d '\n') \ --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS) else $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(call pretty,"Target vendor_boot image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) endif $(call declare-1p-container,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DTB_IMAGE_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(INTERNAL_VENDOR_BOOTCONDIG_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) else # BUILDING_VENDOR_BOOT_IMAGE not defined, use prebuilt image ifdef BOARD_PREBUILT_VENDOR_BOOTIMAGE INTERNAL_PREBUILT_VENDOR_BOOTIMAGE := $(BOARD_PREBUILT_VENDOR_BOOTIMAGE) INSTALLED_VENDOR_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot.img ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_KEY_PATH) cp $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $@ chmod +w $@ $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \ --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS) else $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) cp $(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE) $@ endif # BOARD_AVB_ENABLE $(call declare-1p-container,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET),$(INTERNAL_PREBUILT_VENDOR_BOOTIMAGE),$(PRODUCT_OUT)/:/) endif # BOARD_PREBUILT_VENDOR_BOOTIMAGE endif # BUILDING_VENDOR_BOOT_IMAGE # ----------------------------------------------------------------- # vendor kernel boot image ifeq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) INTERNAL_VENDOR_KERNEL_RAMDISK_FILES := $(filter $(TARGET_VENDOR_KERNEL_RAMDISK_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_kernel_boot)/vendor_kernel_ramdisk.cpio$(RAMDISK_EXT) $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS) $(hide) : $(words $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES)) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@ INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_kernel_ramdisk.img $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) @echo "Target vendor kernel ramdisk: $@" $(copy-file-to-target) INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-kernel-ramdisk.txt INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK:.txt=.json) $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_VENDOR_KERNEL_RAMDISK_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK)) INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS := --vendor_ramdisk $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_kernel_boot.img $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET) endif ifdef BOARD_KERNEL_PAGESIZE INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) endif ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_KERNEL_BOOTIMAGE_KEY_PATH) $(call pretty,"Target vendor_kernel_boot image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) $(AVBTOOL) add_hash_footer \ --image $@ \ $(call get-partition-size-argument,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) \ --partition_name vendor_kernel_boot $(INTERNAL_AVB_VENDOR_KERNEL_BOOT_SIGNING_ARGS) \ $(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS) else $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET): $(call pretty,"Target vendor_kernel_boot image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_KERNEL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)) endif $(call declare-1p-container,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),) ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG $(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),\ $(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET) $(INSTALLED_DTBIMAGE_TARGET),\ $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):) else $(call declare-container-license-deps,$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_KERNEL_RAMDISK_TARGET),$(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET):) endif endif # BUILDING_VENDOR_KERNEL_BOOT_IMAGE # ----------------------------------------------------------------- # NOTICE files # # We are required to publish the licenses for all code under BSD, GPL and # Apache licenses (and possibly other more exotic ones as well). We err on the # side of caution, so the licenses for other third-party code are included here # too. # # This needs to be before the systemimage rules, because it adds to # ALL_DEFAULT_INSTALLED_MODULES, which those use to pick which files # go into the systemimage. .PHONY: notice_files # Convert license metadata into xml notice file. # $(1) - Output target notice filename # $(2) - Product name # $(3) - File title # $(4) - License metadata file roots # $(5) - Prefixes to strip # define xml-notice-rule $(1): PRIVATE_PRODUCT := $(2) $(1): PRIVATE_MESSAGE := $(3) $(1): PRIVATE_DEPS := $(call corresponding-license-metadata,$(4)) $(1): $(call corresponding-license-metadata,$(4)) $(XMLNOTICE) $(BUILD_SYSTEM)/Makefile OUT_DIR=$(OUT_DIR) $(XMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $$(PRIVATE_DEPS) notice_files: $(1) endef # Convert license metadata into text notice file. # $(1) - Output target notice filename # $(2) - Product name # $(3) - File title # $(4) - License metadata file roots # $(5) - Prefixes to strip # define text-notice-rule $(1): PRIVATE_PRODUCT := $(2) $(1): PRIVATE_MESSAGE := $(3) $(1): $(call corresponding-license-metadata,$(4)) $(TEXTNOTICE) $(BUILD_SYSTEM)/Makefile OUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4)) notice_files: $(1) endef # Conversion license metadata into html notice file. # $(1) - Output target notice filename # $(2) - Product name # $(3) - File title # $(4) - License metadata file roots # $(5) - Prefixes to strip # define html-notice-rule $(1): PRIVATE_PRODUCT := $(2) $(1): PRIVATE_MESSAGE := $(3) $(1): $(call corresponding-license-metadata,$(4)) $(HTMLNOTICE) $(BUILD_SYSTEM)/Makefile OUT_DIR=$(OUT_DIR) $(HTMLNOTICE) -o $$@ -product=$$(PRIVATE_PRODUCT) -title=$$(PRIVATE_MESSAGE) $(foreach prefix, $(5), -strip_prefix=$(prefix)) $(call corresponding-license-metadata,$(4)) notice_files: $(1) endef $(KATI_obsolete_var combine-notice-files, To create notice files use xml-notice-rule, html-notice-rule, or text-notice-rule.) # Notice file logic isn't relevant for TARGET_BUILD_APPS ifndef TARGET_BUILD_APPS # TODO These intermediate NOTICE.txt/NOTICE.html files should go into # TARGET_OUT_NOTICE_FILES now that the notice files are gathered from # the src subdirectory. kernel_notice_file := $(TARGET_OUT_NOTICE_FILES)/src/kernel.txt # Some targets get included under $(PRODUCT_OUT) for debug symbols or other # reasons--not to be flashed onto any device. Targets under these directories # need no associated notice file on the device UI. exclude_target_dirs := apex # target_notice_file_xml := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml target_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml.gz installed_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.xml.gz target_vendor_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.txt target_vendor_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR.xml.gz installed_vendor_notice_xml_gz := $(TARGET_OUT_VENDOR)/etc/NOTICE.xml.gz target_product_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.txt target_product_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_PRODUCT.xml.gz installed_product_notice_xml_gz := $(TARGET_OUT_PRODUCT)/etc/NOTICE.xml.gz target_system_ext_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.txt target_system_ext_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_EXT.xml.gz installed_system_ext_notice_xml_gz := $(TARGET_OUT_SYSTEM_EXT)/etc/NOTICE.xml.gz target_odm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.txt target_odm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM.xml.gz installed_odm_notice_xml_gz := $(TARGET_OUT_ODM)/etc/NOTICE.xml.gz target_vendor_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.txt target_vendor_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_VENDOR_DLKM.xml.gz installed_vendor_dlkm_notice_xml_gz := $(TARGET_OUT_VENDOR_DLKM)/etc/NOTICE.xml.gz target_odm_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.txt target_odm_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_ODM_DLKM.xml.gz installed_odm_dlkm_notice_xml_gz := $(TARGET_OUT_ODM_DLKM)/etc/NOTICE.xml.gz target_system_dlkm_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.txt target_system_dlkm_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE_SYSTEM_DLKM.xml.gz installed_system_dlkm_notice_xml_gz := $(TARGET_OUT_SYSTEM_DLKM)/etc/NOTICE.xml.gz ALL_INSTALLED_NOTICE_FILES := \ $(installed_notice_html_or_xml_gz) \ $(installed_vendor_notice_xml_gz) \ $(installed_product_notice_xml_gz) \ $(installed_system_ext_notice_xml_gz) \ $(installed_odm_notice_xml_gz) \ $(installed_vendor_dlkm_notice_xml_gz) \ $(installed_odm_dlkm_notice_xml_gz) \ $(installed_system_dlkm_notice_xml_gz) \ # $1 installed file path, e.g. out/target/product/vsoc_x86_64/system_ext/etc/NOTICE.xml.gz define is-notice-file $(if $(filter true,$(PRODUCT_USE_SOONG_NOTICE_XML)),, \ $(if $(findstring $1,$(ALL_INSTALLED_NOTICE_FILES)),Y)) endef # Notice files are copied to TARGET_OUT_NOTICE_FILES as a side-effect of their module # being built. A notice xml file must depend on all modules that could potentially # install a license file relevant to it. license_modules := $(ALL_DEFAULT_INSTALLED_MODULES) $(kernel_notice_file) # Only files copied to a system image need system image notices. license_modules := $(filter $(PRODUCT_OUT)/%,$(license_modules)) # Phonys/fakes don't have notice files (though their deps might) license_modules := $(filter-out $(TARGET_OUT_FAKE)/%,$(license_modules)) # testcases are not relevant to the system image. license_modules := $(filter-out $(TARGET_OUT_TESTCASES)/%,$(license_modules)) # filesystem images: system, vendor, product, system_ext, odm, vendor_dlkm, and odm_dlkm license_modules_system := $(filter $(TARGET_OUT)/%,$(license_modules)) # system_other is relevant to system partition. license_modules_system += $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(license_modules)) license_modules_vendor := $(filter $(TARGET_OUT_VENDOR)/%,$(license_modules)) license_modules_product := $(filter $(TARGET_OUT_PRODUCT)/%,$(license_modules)) license_modules_system_ext := $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(license_modules)) license_modules_odm := $(filter $(TARGET_OUT_ODM)/%,$(license_modules)) license_modules_vendor_dlkm := $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(license_modules)) license_modules_odm_dlkm := $(filter $(TARGET_OUT_ODM_DLKM)/%,$(license_modules)) license_modules_odm_dlkm := $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(license_modules)) license_modules_agg := $(license_modules_system) \ $(license_modules_vendor) \ $(license_modules_product) \ $(license_modules_system_ext) \ $(license_modules_odm) \ $(license_modules_vendor_dlkm) \ $(license_modules_odm_dlkm) \ $(license_modules_system_dlkm) # targets used for debug symbols only and do not get copied to the device license_modules_symbols_only := $(filter $(PRODUCT_OUT)/apex/%,$(license_modules)) license_modules_rest := $(filter-out $(license_modules_agg),$(license_modules)) license_modules_rest := $(filter-out $(license_modules_symbols_only),$(license_modules_rest)) # Identify the other targets we expect to have notices for: # targets copied to the device but are not readable by the UI (e.g. must boot # into a different partition to read or don't have an associated /etc # directory) must have their notices built somewhere readable. license_modules_rehomed := $(filter-out $(PRODUCT_OUT)/%/%,$(license_modules_rest)) # files in root have no /etc license_modules_rehomed += $(filter $(PRODUCT_OUT)/recovery/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/root/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/data/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/ramdisk/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/debug_ramdisk/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/vendor_ramdisk/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/persist/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/persist.img,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/system_other/%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/kernel%,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/%.img,$(license_modules_rest)) license_modules_rehomed += $(filter $(PRODUCT_OUT)/%.bin,$(license_modules_rest)) # after removing targets in system images, targets reported in system images, and # targets used for debug symbols that do not need notices, nothing must remain. license_modules_rest := $(filter-out $(license_modules_rehomed),$(license_modules_rest)) $(call maybe-print-list-and-error, $(license_modules_rest), \ "Targets added under $(PRODUCT_OUT)/ unaccounted for notice handling.") # If we are building in a configuration that includes a prebuilt vendor.img, we can't # update its notice file, so include those notices in the system partition instead ifdef BOARD_PREBUILT_VENDORIMAGE license_modules_system += $(license_modules_rehomed) system_xml_directories := xml_excluded_vendor_product_odm_vendor_dlkm_odm_dlkm system_notice_file_message := "Notices for files contained in all filesystem images except vendor/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:" else license_modules_vendor += $(license_modules_rehomed) system_xml_directories := xml_system system_notice_file_message := "Notices for files contained in the system filesystem image in this directory:" endif ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz) need_vendor_notice:=false ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) need_vendor_notice:=true endif ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE need_vendor_notice:=true endif ifdef BUILDING_VENDOR_IMAGE need_vendor_notice:=true endif ifeq (true,$(need_vendor_notice)) ifneq (,$(installed_vendor_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_notice_xml_gz) endif endif need_vendor_notice:= ifdef BUILDING_ODM_IMAGE ifneq (,$(installed_odm_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_notice_xml_gz) endif endif ifdef BUILDING_PRODUCT_IMAGE ifneq (,$(installed_product_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_product_notice_xml_gz) endif endif ifdef BUILDING_SYSTEM_EXT_IMAGE ifneq (,$(installed_system_ext_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_system_ext_notice_xml_gz) endif endif ifdef BUILDING_VENDOR_DLKM_IMAGE ifneq (,$(installed_vendor_dlkm_notice_xml_gz) ALL_DEFAULT_INSTALLED_MODULES += $(installed_vendor_dlkm_notice_xml_gz) endif endif ifdef BUILDING_ODM_DLKM_IMAGE ifneq (,$(installed_odm_dlkm_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_odm_dlkm_notice_xml_gz) endif endif ifdef BUILDING_SYSTEM_DLKM_IMAGE ifneq (,$(installed_system_dlkm_notice_xml_gz)) ALL_DEFAULT_INSTALLED_MODULES += $(installed_system_dlkm_notice_xml_gz) endif endif endif # TARGET_BUILD_APPS # Presently none of the prebuilts etc. comply with policy to have a license text. Fake one here. $(eval $(call copy-one-file,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,$(kernel_notice_file))) ifneq (,$(strip $(INSTALLED_KERNEL_TARGET))) $(call declare-license-metadata,$(INSTALLED_KERNEL_TARGET),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel) endif # No matter where it gets copied from, a copied linux kernel is licensed under "GPL 2.0 only" $(eval $(call declare-copy-files-license-metadata,,:kernel,SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,kernel)) # ################################################################# # Targets for user images # ################################################################# # These options tell the recovery updater/installer how to mount the partitions writebale. # =[|]... # fstype_opts := [,]... # opt := [=] # The following worked on Nexus devices with Kernel 3.1, 3.4, 3.10 DEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS := ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc INTERNAL_USERIMAGES_DEPS := \ $(BUILD_IMAGE) \ $(MKE2FS_CONF) \ $(MKEXTUSERIMG) $(call declare-1p-target,$(MKE2FS_CONF),system/extras) ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) endif ifneq ($(filter \ $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ ,erofs),) INTERNAL_USERIMAGES_DEPS += $(MKEROFS) ifeq ($(BOARD_EROFS_USE_LEGACY_COMPRESSION),true) BOARD_EROFS_COMPRESSOR ?= "lz4" else BOARD_EROFS_COMPRESSOR ?= "lz4hc,9" endif endif ifneq ($(filter \ $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ $(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE) \ ,squashfs),) INTERNAL_USERIMAGES_DEPS += $(MKSQUASHFSUSERIMG) endif ifeq ($(BOARD_AVB_ENABLE),true) INTERNAL_USERIMAGES_DEPS += $(AVBTOOL) endif # Get a colon-separated list of search paths. INTERNAL_USERIMAGES_BINARY_PATHS := $(subst $(space),:,$(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))) SELINUX_FC := $(call intermediates-dir-for,ETC,file_contexts.bin)/file_contexts.bin INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC) # $(1) the partition name (eg system) # $(2) the image prop file define add-common-flags-to-image-props $(eval _var := $(call to-upper,$(1))) $(hide) echo "$(1)_selinux_fc=$(SELINUX_FC)" >> $(2) $(hide) echo "building_$(1)_image=$(BUILDING_$(_var)_IMAGE)" >> $(2) endef # $(1) the partition name (eg system) # $(2) the image prop file define add-common-ro-flags-to-image-props $(eval _var := $(call to-upper,$(1))) $(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR),$(hide) echo "$(1)_erofs_compressor=$(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESS_HINTS),$(hide) echo "$(1)_erofs_compress_hints=$(BOARD_$(_var)IMAGE_EROFS_COMPRESS_HINTS)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE),$(hide) echo "$(1)_erofs_pcluster_size=$(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_EROFS_BLOCKSIZE),$(hide) echo "$(1)_erofs_blocksize=$(BOARD_$(_var)IMAGE_EROFS_BLOCKSIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo "$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo "$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE),$(hide) echo "$(1)_f2fs_blocksize=$(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo "$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo "$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "$(1)_reserved_size=$(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_PARTITION_SIZE),$(hide) echo "$(1)_size=$(BOARD_$(_var)IMAGE_PARTITION_SIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "$(1)_squashfs_block_size=$(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "$(1)_squashfs_compressor=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "$(1)_squashfs_compressor_opt=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(2)) $(if $(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "$(1)_squashfs_disable_4k_align=$(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(2)) $(if $(PRODUCT_$(_var)_BASE_FS_PATH),$(hide) echo "$(1)_base_fs_file=$(PRODUCT_$(_var)_BASE_FS_PATH)" >> $(2)) $(eval _size := $(BOARD_$(_var)IMAGE_PARTITION_SIZE)) $(eval _reserved := $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)) $(eval _headroom := $(PRODUCT_$(_var)_HEADROOM)) $(if $(or $(_size), $(_reserved), $(_headroom)),, $(hide) echo "$(1)_disable_sparse=true" >> $(2)) $(call add-common-flags-to-image-props,$(1),$(2)) endef # $(1): the path of the output dictionary file # $(2): a subset of "system vendor cache userdata product system_ext oem odm vendor_dlkm odm_dlkm system_dlkm" # $(3): additional "key=value" pairs to append to the dictionary file. define generate-image-prop-dictionary $(if $(filter $(2),system),\ $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1)) $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1)) $(call add-common-ro-flags-to-image-props,system,$(1)) ) $(if $(filter $(2),system_other),\ $(hide) echo "building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)" >> $(1) $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),, $(hide) echo "system_other_disable_sparse=true" >> $(1)) ) $(if $(filter $(2),userdata),\ $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1)) $(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(1)) $(if $(PRODUCT_FS_CASEFOLD),$(hide) echo "needs_casefold=$(PRODUCT_FS_CASEFOLD)" >> $(1)) $(if $(PRODUCT_QUOTA_PROJID),$(hide) echo "needs_projid=$(PRODUCT_QUOTA_PROJID)" >> $(1)) $(if $(PRODUCT_FS_COMPRESSION),$(hide) echo "needs_compress=$(PRODUCT_FS_COMPRESSION)" >> $(1)) $(call add-common-flags-to-image-props,userdata,$(1)) ) $(if $(filter $(2),cache),\ $(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1)) $(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1)) $(call add-common-flags-to-image-props,cache,$(1)) ) $(if $(filter $(2),vendor),\ $(call add-common-ro-flags-to-image-props,vendor,$(1)) ) $(if $(filter $(2),product),\ $(call add-common-ro-flags-to-image-props,product,$(1)) ) $(if $(filter $(2),system_ext),\ $(call add-common-ro-flags-to-image-props,system_ext,$(1)) ) $(if $(filter $(2),odm),\ $(call add-common-ro-flags-to-image-props,odm,$(1)) ) $(if $(filter $(2),vendor_dlkm),\ $(call add-common-ro-flags-to-image-props,vendor_dlkm,$(1)) ) $(if $(filter $(2),odm_dlkm),\ $(call add-common-ro-flags-to-image-props,odm_dlkm,$(1)) ) $(if $(filter $(2),system_dlkm),\ $(call add-common-ro-flags-to-image-props,system_dlkm,$(1)) ) $(if $(filter $(2),oem),\ $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1)) $(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1)) $(if $(BOARD_OEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "oem_extfs_inode_count=$(BOARD_OEMIMAGE_EXTFS_INODE_COUNT)" >> $(1)) $(if $(BOARD_OEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "oem_extfs_rsv_pct=$(BOARD_OEMIMAGE_EXTFS_RSV_PCT)" >> $(1)) $(call add-common-flags-to-image-props,oem,$(1)) ) $(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(1) $(if $(filter true,$(TARGET_USERIMAGES_USE_EXT2)),$(hide) echo "fs_type=ext2" >> $(1), $(if $(filter true,$(TARGET_USERIMAGES_USE_EXT3)),$(hide) echo "fs_type=ext3" >> $(1), $(if $(filter true,$(TARGET_USERIMAGES_USE_EXT4)),$(hide) echo "fs_type=ext4" >> $(1)))) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)),,$(hide) echo "extfs_sparse_flag=-s" >> $(1)) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED)),,$(hide) echo "erofs_sparse_flag=-s" >> $(1)) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED)),,$(hide) echo "squashfs_sparse_flag=-s" >> $(1)) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)),,$(hide) echo "f2fs_sparse_flag=-S" >> $(1)) $(if $(BOARD_EROFS_COMPRESSOR),$(hide) echo "erofs_default_compressor=$(BOARD_EROFS_COMPRESSOR)" >> $(1)) $(if $(BOARD_EROFS_COMPRESS_HINTS),$(hide) echo "erofs_default_compress_hints=$(BOARD_EROFS_COMPRESS_HINTS)" >> $(1)) $(if $(BOARD_EROFS_PCLUSTER_SIZE),$(hide) echo "erofs_pcluster_size=$(BOARD_EROFS_PCLUSTER_SIZE)" >> $(1)) $(if $(BOARD_EROFS_BLOCKSIZE),$(hide) echo "erofs_blocksize=$(BOARD_EROFS_BLOCKSIZE)" >> $(1)) $(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo "erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)" >> $(1)) $(if $(BOARD_EROFS_USE_LEGACY_COMPRESSION),$(hide) echo "erofs_use_legacy_compression=$(BOARD_EROFS_USE_LEGACY_COMPRESSION)" >> $(1)) $(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1)) $(if $(BOARD_F2FS_BLOCKSIZE),$(hide) echo "f2fs_blocksize=$(BOARD_F2FS_BLOCKSIZE)" >> $(1)) $(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo "flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)" >> $(1)) $(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo "flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)" >> $(1)) $(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo "verity_disable=true" >> $(1)) $(if $(PRODUCT_SYSTEM_VERITY_PARTITION),$(hide) echo "system_verity_block_device=$(PRODUCT_SYSTEM_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo "vendor_verity_block_device=$(PRODUCT_VENDOR_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_PRODUCT_VERITY_PARTITION),$(hide) echo "product_verity_block_device=$(PRODUCT_PRODUCT_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_SYSTEM_EXT_VERITY_PARTITION),$(hide) echo "system_ext_verity_block_device=$(PRODUCT_SYSTEM_EXT_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_VENDOR_DLKM_VERITY_PARTITION),$(hide) echo "vendor_dlkm_verity_block_device=$(PRODUCT_VENDOR_DLKM_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_ODM_DLKM_VERITY_PARTITION),$(hide) echo "odm_dlkm_verity_block_device=$(PRODUCT_ODM_DLKM_VERITY_PARTITION)" >> $(1)) $(if $(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION),$(hide) echo "system_dlkm_verity_block_device=$(PRODUCT_SYSTEM_DLKM_VERITY_PARTITION)" >> $(1)) $(if $(BOARD_AVB_ENABLE), \ $(hide) echo "avb_avbtool=$(notdir $(AVBTOOL))" >> $(1)$(newline) \ $(if $(filter $(2),system), \ $(hide) echo "avb_system_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_system_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_SYSTEM_KEY_PATH), \ $(hide) echo "avb_system_key_path=$(BOARD_AVB_SYSTEM_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_system_algorithm=$(BOARD_AVB_SYSTEM_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_system_rollback_index_location=$(BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),system_other), \ $(hide) echo "avb_system_other_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_system_other_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH),\ $(hide) echo "avb_system_other_key_path=$(BOARD_AVB_SYSTEM_OTHER_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_system_other_algorithm=$(BOARD_AVB_SYSTEM_OTHER_ALGORITHM)" >> $(1)$(newline))) \ $(if $(filter $(2),vendor), \ $(hide) echo "avb_vendor_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_VENDOR_KEY_PATH),\ $(hide) echo "avb_vendor_key_path=$(BOARD_AVB_VENDOR_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_algorithm=$(BOARD_AVB_VENDOR_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_rollback_index_location=$(BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),product), \ $(hide) echo "avb_product_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_product_add_hashtree_footer_args=$(BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\ $(hide) echo "avb_product_key_path=$(BOARD_AVB_PRODUCT_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_product_algorithm=$(BOARD_AVB_PRODUCT_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_product_rollback_index_location=$(BOARD_AVB_PRODUCT_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),system_ext), \ $(hide) echo "avb_system_ext_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_system_ext_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\ $(hide) echo "avb_system_ext_key_path=$(BOARD_AVB_SYSTEM_EXT_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_system_ext_algorithm=$(BOARD_AVB_SYSTEM_EXT_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_system_ext_rollback_index_location=$(BOARD_AVB_SYSTEM_EXT_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),odm), \ $(hide) echo "avb_odm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_odm_add_hashtree_footer_args=$(BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_ODM_KEY_PATH),\ $(hide) echo "avb_odm_key_path=$(BOARD_AVB_ODM_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_odm_algorithm=$(BOARD_AVB_ODM_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_odm_rollback_index_location=$(BOARD_AVB_ODM_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),vendor_dlkm), \ $(hide) echo "avb_vendor_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_dlkm_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\ $(hide) echo "avb_vendor_dlkm_key_path=$(BOARD_AVB_VENDOR_DLKM_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_dlkm_algorithm=$(BOARD_AVB_VENDOR_DLKM_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_vendor_dlkm_rollback_index_location=$(BOARD_AVB_VENDOR_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),odm_dlkm), \ $(hide) echo "avb_odm_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_odm_dlkm_add_hashtree_footer_args=$(BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\ $(hide) echo "avb_odm_dlkm_key_path=$(BOARD_AVB_ODM_DLKM_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_odm_dlkm_algorithm=$(BOARD_AVB_ODM_DLKM_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_odm_dlkm_rollback_index_location=$(BOARD_AVB_ODM_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ $(if $(filter $(2),system_dlkm), \ $(hide) echo "avb_system_dlkm_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1)$(newline) \ $(hide) echo "avb_system_dlkm_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS)" >> $(1)$(newline) \ $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\ $(hide) echo "avb_system_dlkm_key_path=$(BOARD_AVB_SYSTEM_DLKM_KEY_PATH)" >> $(1)$(newline) \ $(hide) echo "avb_system_dlkm_algorithm=$(BOARD_AVB_SYSTEM_DLKM_ALGORITHM)" >> $(1)$(newline) \ $(hide) echo "avb_system_dlkm_rollback_index_location=$(BOARD_SYSTEM_SYSTEM_DLKM_ROLLBACK_INDEX_LOCATION)" >> $(1)$(newline))) \ ) $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\ $(hide) echo "recovery_as_boot=true" >> $(1)) $(hide) echo "root_dir=$(TARGET_ROOT_OUT)" >> $(1) $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)),\ $(hide) echo "use_dynamic_partition_size=true" >> $(1)) $(if $(USE_FIXED_TIMESTAMP_IMG_FILES)$(COPY_IMAGES_FOR_TARGET_FILES_ZIP),\ $(hide) echo "use_fixed_timestamp=true" >> $(1)) $(if $(3),$(hide) $(foreach kv,$(3),echo "$(kv)" >> $(1);)) $(hide) sort -o $(1) $(1) endef # $(1): the path of the output dictionary file # $(2): additional "key=value" pairs to append to the dictionary file. PROP_DICTIONARY_IMAGES := oem ifdef BUILDING_CACHE_IMAGE PROP_DICTIONARY_IMAGES += cache endif ifdef BUILDING_SYSTEM_IMAGE PROP_DICTIONARY_IMAGES += system endif ifdef BUILDING_SYSTEM_OTHER_IMAGE PROP_DICTIONARY_IMAGES += system_other endif ifdef BUILDING_USERDATA_IMAGE PROP_DICTIONARY_IMAGES += userdata endif ifdef BUILDING_VENDOR_IMAGE PROP_DICTIONARY_IMAGES += vendor endif ifdef BUILDING_PRODUCT_IMAGE PROP_DICTIONARY_IMAGES += product endif ifdef BUILDING_SYSTEM_EXT_IMAGE PROP_DICTIONARY_IMAGES += system_ext endif ifdef BUILDING_ODM_IMAGE PROP_DICTIONARY_IMAGES += odm endif ifdef BUILDING_VENDOR_DLKM_IMAGE PROP_DICTIONARY_IMAGES += vendor_dlkm endif ifdef BUILDING_ODM_DLKM_IMAGE PROP_DICTIONARY_IMAGES += odm_dlkm endif ifdef BUILDING_SYSTEM_DLKM_IMAGE PROP_DICTIONARY_IMAGES += system_dlkm endif define generate-userimage-prop-dictionary $(call generate-image-prop-dictionary,$(1),$(PROP_DICTIONARY_IMAGES),$(2)) endef # $(1): the path of the input dictionary file, where each line has the format key=value # $(2): the key to look up define read-image-prop-dictionary $$(grep '$(2)=' $(1) | cut -f2- -d'=') endef # ----------------------------------------------------------------- # Recovery image # Recovery image exists if we are building recovery, or building recovery as boot. INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RECOVERY_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_RECOVERY_IMAGE INTERNAL_RECOVERYIMAGE_FILES := $(filter $(TARGET_RECOVERY_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_RECOVERY := $(PRODUCT_OUT)/installed-files-recovery.txt INSTALLED_FILES_JSON_RECOVERY := $(INSTALLED_FILES_FILE_RECOVERY:.txt=.json) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET) endif # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other # INSTALLED_FILES_FILE_* rules. Because currently there're cp/rsync/rm commands in # build-recoveryimage-target, which would touch the files under TARGET_RECOVERY_OUT and race with # the call to FILELIST. $(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) $(INSTALLED_FILES_FILE_RECOVERY): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RECOVERY) $(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERYIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_RECOVERY_ROOT_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_RECOVERY))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_RECOVERY))) recovery_sepolicy := \ $(TARGET_RECOVERY_ROOT_OUT)/sepolicy \ $(TARGET_RECOVERY_ROOT_OUT)/plat_file_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/plat_service_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/plat_property_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/system_ext_file_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/system_ext_service_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/system_ext_property_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/vendor_file_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/vendor_service_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/vendor_property_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/odm_file_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/odm_property_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/product_file_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/product_service_contexts \ $(TARGET_RECOVERY_ROOT_OUT)/product_property_contexts # Passed into rsync from non-recovery root to recovery root, to avoid overwriting recovery-specific # SELinux files IGNORE_RECOVERY_SEPOLICY := $(patsubst $(TARGET_RECOVERY_OUT)/%,--exclude=/%,$(recovery_sepolicy)) # if building multiple boot images from multiple kernels, use the first kernel listed # for the recovery image recovery_kernel := $(firstword $(INSTALLED_KERNEL_TARGET)) recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img recovery_resources_common := bootable/recovery/res # Set recovery_density to a density bucket based on TARGET_SCREEN_DENSITY, PRODUCT_AAPT_PREF_CONFIG, # or mdpi, in order of preference. We support both specific buckets (e.g. xdpi) and numbers, # which get remapped to a bucket. recovery_density := $(or $(TARGET_SCREEN_DENSITY),$(PRODUCT_AAPT_PREF_CONFIG),mdpi) ifeq (,$(filter xxxhdpi xxhdpi xhdpi hdpi mdpi,$(recovery_density))) recovery_density_value := $(patsubst %dpi,%,$(recovery_density)) # We roughly use the medium point between the primary densities to split buckets. # ------160------240------320----------480------------640------ # mdpi hdpi xhdpi xxhdpi xxxhdpi recovery_density := $(strip \ $(or $(if $(filter $(shell echo $$(($(recovery_density_value) >= 560))),1),xxxhdpi),\ $(if $(filter $(shell echo $$(($(recovery_density_value) >= 400))),1),xxhdpi),\ $(if $(filter $(shell echo $$(($(recovery_density_value) >= 280))),1),xhdpi),\ $(if $(filter $(shell echo $$(($(recovery_density_value) >= 200))),1),hdpi,mdpi))) endif ifneq (,$(wildcard $(recovery_resources_common)-$(recovery_density))) recovery_resources_common := $(recovery_resources_common)-$(recovery_density) else recovery_resources_common := $(recovery_resources_common)-xhdpi endif # Select the 18x32 font on high-density devices (xhdpi and up); and the 12x22 font on other devices. # Note that the font selected here can be overridden for a particular device by putting a font.png # in its private recovery resources. ifneq (,$(filter xxxhdpi xxhdpi xhdpi,$(recovery_density))) recovery_font := bootable/recovery/fonts/18x32.png else recovery_font := bootable/recovery/fonts/12x22.png endif # We will only generate the recovery background text images if the variable # TARGET_RECOVERY_UI_SCREEN_WIDTH is defined. For devices with xxxhdpi and xxhdpi, we set the # variable to the commonly used values here, if it hasn't been intialized elsewhere. While for # devices with lower density, they must have TARGET_RECOVERY_UI_SCREEN_WIDTH defined in their # BoardConfig in order to use this feature. ifeq ($(recovery_density),xxxhdpi) TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1440 else ifeq ($(recovery_density),xxhdpi) TARGET_RECOVERY_UI_SCREEN_WIDTH ?= 1080 endif ifneq ($(TARGET_RECOVERY_UI_SCREEN_WIDTH),) # Subtracts the margin width and menu indent from the screen width; it's safe to be conservative. ifeq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),) recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - 10)) else recovery_image_width := $$(($(TARGET_RECOVERY_UI_SCREEN_WIDTH) - $(TARGET_RECOVERY_UI_MARGIN_WIDTH) - 10)) endif RECOVERY_INSTALLING_TEXT_FILE := $(call intermediates-dir-for,ETC,recovery_text_res)/installing_text.png RECOVERY_INSTALLING_SECURITY_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/installing_security_text.png RECOVERY_ERASING_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/erasing_text.png RECOVERY_ERROR_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/error_text.png RECOVERY_NO_COMMAND_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/no_command_text.png RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/cancel_wipe_data_text.png RECOVERY_FACTORY_DATA_RESET_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/factory_data_reset_text.png RECOVERY_TRY_AGAIN_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/try_again_text.png RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_confirmation_text.png RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := $(dir $(RECOVERY_INSTALLING_TEXT_FILE))/wipe_data_menu_header_text.png generated_recovery_text_files := \ $(RECOVERY_INSTALLING_TEXT_FILE) \ $(RECOVERY_INSTALLING_SECURITY_TEXT_FILE) \ $(RECOVERY_ERASING_TEXT_FILE) \ $(RECOVERY_ERROR_TEXT_FILE) \ $(RECOVERY_NO_COMMAND_TEXT_FILE) \ $(RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE) \ $(RECOVERY_FACTORY_DATA_RESET_TEXT_FILE) \ $(RECOVERY_TRY_AGAIN_TEXT_FILE) \ $(RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE) \ $(RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE) resource_dir := bootable/recovery/tools/recovery_l10n/res/ resource_dir_deps := $(sort $(shell find $(resource_dir) -name *.xml -not -name .*)) image_generator_jar := $(HOST_OUT_JAVA_LIBRARIES)/RecoveryImageGenerator.jar zopflipng := $(HOST_OUT_EXECUTABLES)/zopflipng $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_SOURCE_FONTS := $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_FONT_FILES_DIR := $(call intermediates-dir-for,ETC,recovery_font_files) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RESOURCE_DIR := $(resource_dir) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_IMAGE_GENERATOR_JAR := $(image_generator_jar) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_ZOPFLIPNG := $(zopflipng) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_IMAGE_WIDTH := $(recovery_image_width) $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST := \ recovery_installing \ recovery_installing_security \ recovery_erasing \ recovery_error \ recovery_no_command $(RECOVERY_INSTALLING_TEXT_FILE): PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST := \ recovery_cancel_wipe_data \ recovery_factory_data_reset \ recovery_try_again \ recovery_wipe_data_menu_header \ recovery_wipe_data_confirmation $(RECOVERY_INSTALLING_TEXT_FILE): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(RECOVERY_INSTALLING_TEXT_FILE),$(generated_recovery_text_files)) $(RECOVERY_INSTALLING_TEXT_FILE): $(image_generator_jar) $(resource_dir_deps) $(recovery_noto-fonts_dep) $(recovery_roboto-fonts_dep) $(zopflipng) # Prepares the font directory. @rm -rf $(PRIVATE_RECOVERY_FONT_FILES_DIR) @mkdir -p $(PRIVATE_RECOVERY_FONT_FILES_DIR) $(foreach filename,$(PRIVATE_SOURCE_FONTS), cp $(filename) $(PRIVATE_RECOVERY_FONT_FILES_DIR) &&) true @rm -rf $(dir $@) @mkdir -p $(dir $@) $(foreach text_name,$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST) $(PRIVATE_RECOVERY_WIPE_DATA_TEXT_LIST), \ $(eval output_file := $(dir $@)/$(patsubst recovery_%,%_text.png,$(text_name))) \ $(eval center_alignment := $(if $(filter $(text_name),$(PRIVATE_RECOVERY_BACKGROUND_TEXT_LIST)), --center_alignment)) \ java -jar $(PRIVATE_IMAGE_GENERATOR_JAR) \ --image_width $(PRIVATE_RECOVERY_IMAGE_WIDTH) \ --text_name $(text_name) \ --font_dir $(PRIVATE_RECOVERY_FONT_FILES_DIR) \ --resource_dir $(PRIVATE_RESOURCE_DIR) \ --output_file $(output_file) $(center_alignment) && \ $(PRIVATE_ZOPFLIPNG) -y --iterations=1 --filters=0 $(output_file) $(output_file) > /dev/null &&) true else RECOVERY_INSTALLING_TEXT_FILE := RECOVERY_INSTALLING_SECURITY_TEXT_FILE := RECOVERY_ERASING_TEXT_FILE := RECOVERY_ERROR_TEXT_FILE := RECOVERY_NO_COMMAND_TEXT_FILE := RECOVERY_CANCEL_WIPE_DATA_TEXT_FILE := RECOVERY_FACTORY_DATA_RESET_TEXT_FILE := RECOVERY_TRY_AGAIN_TEXT_FILE := RECOVERY_WIPE_DATA_CONFIRMATION_TEXT_FILE := RECOVERY_WIPE_DATA_MENU_HEADER_TEXT_FILE := endif # TARGET_RECOVERY_UI_SCREEN_WIDTH ifndef TARGET_PRIVATE_RES_DIRS TARGET_PRIVATE_RES_DIRS := $(wildcard $(TARGET_DEVICE_DIR)/recovery/res) endif recovery_resource_deps := $(shell find $(recovery_resources_common) \ $(TARGET_PRIVATE_RES_DIRS) -type f -not -name "*.bp") recovery_resource_deps += $(generated_recovery_text_files) ifdef TARGET_RECOVERY_FSTAB recovery_fstab := $(TARGET_RECOVERY_FSTAB) else ifdef TARGET_RECOVERY_FSTAB_GENRULE # Specifies a soong genrule module that generates an fstab. recovery_fstab := $(call intermediates-dir-for,ETC,$(TARGET_RECOVERY_FSTAB_GENRULE))/$(TARGET_RECOVERY_FSTAB_GENRULE) else recovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab)) endif ifdef TARGET_RECOVERY_WIPE recovery_wipe := $(TARGET_RECOVERY_WIPE) else recovery_wipe := endif # Traditionally with non-A/B OTA we have: # boot.img + recovery-from-boot.p + recovery-resource.dat = recovery.img. # recovery-resource.dat is needed only if we carry an imgdiff patch of the boot and recovery images # and invoke install-recovery.sh on the first boot post an OTA update. # # We no longer need that if one of the following conditions holds: # a) We carry a full copy of the recovery image - no patching needed # (BOARD_USES_FULL_RECOVERY_IMAGE = true); # b) We build a single image that contains boot and recovery both - no recovery image to install # (BOARD_USES_RECOVERY_AS_BOOT = true); # c) We include the recovery DTBO image within recovery - not needing the resource file as we # do bsdiff because boot and recovery will contain different number of entries # (BOARD_INCLUDE_RECOVERY_DTBO = true). # d) We include the recovery ACPIO image within recovery - not needing the resource file as we # do bsdiff because boot and recovery will contain different number of entries # (BOARD_INCLUDE_RECOVERY_ACPIO = true). # e) We build a single image that contains vendor_boot and recovery both - no recovery image to # install # (BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT = true). ifeq (,$(filter true, $(BOARD_USES_FULL_RECOVERY_IMAGE) $(BOARD_USES_RECOVERY_AS_BOOT) \ $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO) \ $(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) # Named '.dat' so we don't attempt to use imgdiff for patching it. RECOVERY_RESOURCE_ZIP := $(TARGET_OUT_VENDOR)/etc/recovery-resource.dat ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_RESOURCE_ZIP) else RECOVERY_RESOURCE_ZIP := endif INSTALLED_RECOVERY_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/prop.default $(INSTALLED_RECOVERY_BUILD_PROP_TARGET): PRIVATE_RECOVERY_UI_PROPERTIES := \ TARGET_RECOVERY_UI_ANIMATION_FPS:animation_fps \ TARGET_RECOVERY_UI_MARGIN_HEIGHT:margin_height \ TARGET_RECOVERY_UI_MARGIN_WIDTH:margin_width \ TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS:menu_unusable_rows \ TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE:progress_bar_baseline \ TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD:touch_low_threshold \ TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD:touch_high_threshold \ TARGET_RECOVERY_UI_VR_STEREO_OFFSET:vr_stereo_offset \ TARGET_RECOVERY_UI_BRIGHTNESS_FILE:brightness_file \ TARGET_RECOVERY_UI_MAX_BRIGHTNESS_FILE:max_brightness_file \ TARGET_RECOVERY_UI_BRIGHTNESS_NORMAL:brightness_normal_percent \ TARGET_RECOVERY_UI_BRIGHTNESS_DIMMED:brightness_dimmed_percent # Parses the given list of build variables and writes their values as build properties if defined. # For example, if a target defines `TARGET_RECOVERY_UI_MARGIN_HEIGHT := 100`, # `ro.recovery.ui.margin_height=100` will be appended to the given output file. # $(1): Map from the build variable names to property names # $(2): Output file define append-recovery-ui-properties echo "#" >> $(2) echo "# RECOVERY UI BUILD PROPERTIES" >> $(2) echo "#" >> $(2) $(foreach prop,$(1), \ $(eval _varname := $(call word-colon,1,$(prop))) \ $(eval _propname := $(call word-colon,2,$(prop))) \ $(eval _value := $($(_varname))) \ $(if $(_value), \ echo ro.recovery.ui.$(_propname)=$(_value) >> $(2) &&)) true endef $(INSTALLED_RECOVERY_BUILD_PROP_TARGET): \ $(INSTALLED_BUILD_PROP_TARGET) \ $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \ $(INSTALLED_ODM_BUILD_PROP_TARGET) \ $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) \ $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) @echo "Target recovery buildinfo: $@" $(hide) mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cat $(INSTALLED_BUILD_PROP_TARGET) >> $@ $(hide) cat $(INSTALLED_VENDOR_BUILD_PROP_TARGET) >> $@ $(hide) cat $(INSTALLED_ODM_BUILD_PROP_TARGET) >> $@ $(hide) cat $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) >> $@ $(hide) cat $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) >> $@ $(call append-recovery-ui-properties,$(PRIVATE_RECOVERY_UI_PROPERTIES),$@) $(call declare-1p-target,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),build) $(call declare-license-deps,$(INSTALLED_RECOVERY_BUILD_PROP_TARGET),\ $(INSTALLED_BUILD_PROP_TARGET) $(INSTALLED_VENDOR_BUILD_PROP_TARGET) $(INSTALLED_ODM_BUILD_PROP_TARGET) \ $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET)) # Only install boot/etc/build.prop to recovery image on recovery_as_boot. # On device with dedicated recovery partition, the file should come from the boot # ramdisk. ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH) $(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET): $(INSTALLED_RAMDISK_BUILD_PROP_TARGET) $(copy-file-to-target) $(call declare-1p-target,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),build) $(call declare-license-deps,$(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET),$(INSTALLED_RAMDISK_BUILD_PROP_TARGET)) endif INTERNAL_RECOVERYIMAGE_ARGS := --ramdisk $(recovery_ramdisk) ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT))) INTERNAL_RECOVERYIMAGE_ARGS += $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) # Assumes this has already been stripped ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) ifdef INTERNAL_KERNEL_CMDLINE INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)" endif # INTERNAL_KERNEL_CMDLINE != "" endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true ifdef BOARD_KERNEL_BASE INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE) endif ifdef BOARD_KERNEL_PAGESIZE INTERNAL_RECOVERYIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE) endif ifdef BOARD_INCLUDE_RECOVERY_DTBO ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) else INTERNAL_RECOVERYIMAGE_ARGS += --recovery_dtbo $(BOARD_PREBUILT_DTBOIMAGE) endif endif # BOARD_INCLUDE_RECOVERY_DTBO ifdef BOARD_INCLUDE_RECOVERY_ACPIO INTERNAL_RECOVERYIMAGE_ARGS += --recovery_acpio $(BOARD_RECOVERY_ACPIO) endif ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG INTERNAL_RECOVERYIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET) endif endif # (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT) ifndef BOARD_RECOVERY_MKBOOTIMG_ARGS BOARD_RECOVERY_MKBOOTIMG_ARGS := $(BOARD_MKBOOTIMG_ARGS) endif $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP): $(MKBOOTFS) $(COMPRESSION_COMMAND_DEPS) \ $(INTERNAL_ROOT_FILES) \ $(INSTALLED_RAMDISK_TARGET) \ $(INTERNAL_RECOVERYIMAGE_FILES) \ $(recovery_sepolicy) \ $(INSTALLED_2NDBOOTLOADER_TARGET) \ $(INSTALLED_RECOVERY_BUILD_PROP_TARGET) \ $(INSTALLED_RECOVERY_RAMDISK_BUILD_PROP_TARGET) \ $(recovery_resource_deps) \ $(recovery_fstab) # Making recovery image mkdir -p $(TARGET_RECOVERY_OUT) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sdcard $(TARGET_RECOVERY_ROOT_OUT)/tmp # Copying baseline ramdisk... # Use rsync because "cp -Rf" fails to overwrite broken symlinks on Mac. rsync -a --exclude=sdcard $(IGNORE_RECOVERY_SEPOLICY) $(IGNORE_CACHE_LINK) $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT) # Modifying ramdisk contents... ln -sf /system/bin/init $(TARGET_RECOVERY_ROOT_OUT)/init # Removes $(TARGET_RECOVERY_ROOT_OUT)/init*.rc EXCEPT init.recovery*.rc. find $(TARGET_RECOVERY_ROOT_OUT) -maxdepth 1 -name 'init*.rc' -type f -not -name "init.recovery.*.rc" | xargs rm -f cp $(TARGET_ROOT_OUT)/init.recovery.*.rc $(TARGET_RECOVERY_ROOT_OUT)/ 2> /dev/null || true # Ignore error when the src file doesn't exist. mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/res rm -rf $(TARGET_RECOVERY_ROOT_OUT)/res/* cp -rf $(recovery_resources_common)/* $(TARGET_RECOVERY_ROOT_OUT)/res $(foreach recovery_text_file,$(generated_recovery_text_files), \ cp -rf $(recovery_text_file) $(TARGET_RECOVERY_ROOT_OUT)/res/images/ &&) true cp -f $(recovery_font) $(TARGET_RECOVERY_ROOT_OUT)/res/images/font.png $(foreach item,$(TARGET_PRIVATE_RES_DIRS), \ cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/$(newline)) $(foreach item,$(recovery_fstab), \ cp -f $(item) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab) $(if $(strip $(recovery_wipe)), \ cp -f $(recovery_wipe) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.wipe) ln -sf prop.default $(TARGET_RECOVERY_ROOT_OUT)/default.prop # Silence warnings in first_stage_console. touch $(TARGET_RECOVERY_ROOT_OUT)/linkerconfig/ld.config.txt $(BOARD_RECOVERY_IMAGE_PREPARE) $(hide) touch $@ $(recovery_ramdisk): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RECOVERY_ROOT_OUT) | $(COMPRESSION_COMMAND) > $(recovery_ramdisk) # $(1): output file # $(2): optional kernel file define build-recoveryimage-target $(MKBOOTIMG) $(if $(strip $(2)),--kernel $(strip $(2))) $(INTERNAL_RECOVERYIMAGE_ARGS) \ $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ $(BOARD_RECOVERY_MKBOOTIMG_ARGS) --output $(1) $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \ $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))), \ $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)))) $(if $(filter true,$(BOARD_AVB_ENABLE)), \ $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \ $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS),\ $(AVBTOOL) add_hash_footer --image $(1) $(call get-partition-size-argument,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)) --partition_name recovery $(INTERNAL_AVB_RECOVERY_SIGNING_ARGS) $(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS))) endef recoveryimage-deps := $(MKBOOTIMG) $(recovery_ramdisk) $(recovery_kernel) ifeq (true,$(BOARD_AVB_ENABLE)) recoveryimage-deps += $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) endif ifdef BOARD_INCLUDE_RECOVERY_DTBO ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE recoveryimage-deps += $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) else recoveryimage-deps += $(BOARD_PREBUILT_DTBOIMAGE) endif endif ifdef BOARD_INCLUDE_RECOVERY_ACPIO recoveryimage-deps += $(BOARD_RECOVERY_ACPIO) endif ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG recoveryimage-deps += $(INSTALLED_DTBIMAGE_TARGET) endif ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b))))) $(INSTALLED_BOOTIMAGE_TARGET): $(recoveryimage-deps) $(call pretty,"Target boot image from recovery: $@") $(call build-recoveryimage-target, $@, $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $@)))) $(call declare-container-license-metadata,$(INSTALLED_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",bool) $(call declare-container-license-deps,$(INSTALLED_BOOTIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_BOOTIMAGE_TARGET) endif # BOARD_USES_RECOVERY_AS_BOOT $(INSTALLED_RECOVERYIMAGE_TARGET): $(recoveryimage-deps) $(call build-recoveryimage-target, $@, \ $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel))) ifdef RECOVERY_RESOURCE_ZIP $(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ZIPTIME) $(hide) mkdir -p $(dir $@) $(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrjX $@ -@ $(remove-timestamps-from-package) endif $(call declare-1p-container,$(INSTALLED_RECOVERYIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_RECOVERYIMAGE_TARGET),$(recoveryimage-deps),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_RECOVERYIMAGE_TARGET) .PHONY: recoveryimage-nodeps recoveryimage-nodeps: @echo "make $@: ignoring dependencies" $(call build-recoveryimage-target, $(INSTALLED_RECOVERYIMAGE_TARGET), \ $(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel))) else # BUILDING_RECOVERY_IMAGE RECOVERY_RESOURCE_ZIP := endif # BUILDING_RECOVERY_IMAGE .PHONY: recoveryimage recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP) ifneq ($(BOARD_NAND_PAGE_SIZE),) $(error MTD device is no longer supported and thus BOARD_NAND_PAGE_SIZE is deprecated.) endif ifneq ($(BOARD_NAND_SPARE_SIZE),) $(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.) endif recovery_intermediates := $(call intermediates-dir-for,PACKAGING,recovery) $(eval $(call write-partition-file-list,$(recovery_intermediates)/file_list.txt,$(TARGET_RECOVERY_OUT),$(INTERNAL_RECOVERYIMAGE_FILES))) # ----------------------------------------------------------------- # Build debug ramdisk and debug boot image. INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifneq ($(BUILDING_DEBUG_BOOT_IMAGE)$(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),) INTERNAL_DEBUG_RAMDISK_FILES := $(filter $(TARGET_DEBUG_RAMDISK_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) # Directories to be picked into the debug ramdisk. # As these directories are all merged into one single cpio archive, the order # matters. If there are multiple files with the same pathname, then the last one # wins. # # ramdisk-debug.img will merge the content from either ramdisk.img or # ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT # is set or not. ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RECOVERY_ROOT_OUT) INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(recovery_ramdisk) else # BOARD_USES_RECOVERY_AS_BOOT == true INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RAMDISK_OUT) INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(INSTALLED_RAMDISK_TARGET) endif # BOARD_USES_RECOVERY_AS_BOOT != true INTERNAL_DEBUG_RAMDISK_SRC_DIRS += $(TARGET_DEBUG_RAMDISK_OUT) INTERNAL_DEBUG_RAMDISK_SRC_DEPS := $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET) $(INTERNAL_DEBUG_RAMDISK_FILES) # INSTALLED_FILES_FILE_DEBUG_RAMDISK would ensure TARGET_DEBUG_RAMDISK_OUT is created. INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INTERNAL_DEBUG_RAMDISK_SRC_DEPS) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL) @echo "Installed file list: $@" $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(TARGET_DEBUG_RAMDISK_OUT) touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable $(FILESLIST) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_DEBUG_RAMDISK))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_DEBUG_RAMDISK))) ifdef BUILDING_DEBUG_BOOT_IMAGE # ----------------------------------------------------------------- # the debug ramdisk, which is the original ramdisk plus additional # files: force_debuggable, adb_debug.prop and userdebug sepolicy. # When /force_debuggable is present, /init will load userdebug sepolicy # and property files to allow adb root, if the device is unlocked. INSTALLED_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img $(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) @echo "Target debug ramdisk: $@" $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ $(call declare-1p-container,$(INSTALLED_DEBUG_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DEBUG_RAMDISK_TARGET) .PHONY: ramdisk_debug-nodeps ramdisk_debug-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) @echo "make $@: ignoring dependencies" $(hide) rm -f $(INSTALLED_DEBUG_RAMDISK_TARGET) $(hide) mkdir -p $(dir $(INSTALLED_DEBUG_RAMDISK_TARGET)) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET) # ----------------------------------------------------------------- # the boot-debug.img, which is the kernel plus ramdisk-debug.img # # Note: it's intentional to skip signing for boot-debug.img, because it # can only be used if the device is unlocked with verification error. ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-debug,$(BOARD_KERNEL_BINARIES)), \ $(PRODUCT_OUT)/$(k).img) else INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-debug.img endif # Replace ramdisk.img in $(MKBOOTIMG) ARGS with ramdisk-debug.img to build boot-debug.img $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_RECOVERYIMAGE_ARGS)) else INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_BOOTIMAGE_ARGS)) endif # If boot.img is chained but boot-debug.img is not signed, libavb in bootloader # will fail to find valid AVB metadata from the end of /boot, thus stop booting. # Using a test key to sign boot-debug.img to continue booting with the mismatched # public key, if the device is unlocked. ifneq ($(BOARD_AVB_BOOT_KEY_PATH),) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH) endif BOARD_AVB_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_BOOT_TEST_KEY_PATH) # $(1): the bootimage to sign # $(2): boot image variant (boot, boot-debug, boot-test-harness) define test-key-sign-bootimage $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),$(2)))) $(AVBTOOL) add_hash_footer \ --image $(1) \ $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),$(2)))\ --partition_name boot $(INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS) \ $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) $(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),$(2))) endef # $(1): output file define build-debug-bootimage-target $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-debug,kernel,$(notdir $(1)))) \ $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ $(BOARD_MKBOOTIMG_ARGS) --output $1 $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$1,boot-debug)) endef # Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(AVBTOOL) $(call pretty,"Target boot debug image: $@") $(call build-debug-bootimage-target, $@) $(call declare-container-license-metadata,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),SPDX-license-identifier-GPL-2.0-only SPDX-license-identifier-Apache-2.0,restricted notice,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING build/soong/licenses/LICENSE,"Boot Image",boot) $(call declare-container-license-deps,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(INSTALLED_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) .PHONY: bootimage_debug-nodeps bootimage_debug-nodeps: $(MKBOOTIMG) $(AVBTOOL) echo "make $@: ignoring dependencies" $(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b)) endif # BUILDING_DEBUG_BOOT_IMAGE # ----------------------------------------------------------------- # vendor debug ramdisk # Combines vendor ramdisk files and debug ramdisk files to build the vendor debug ramdisk. INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE INTERNAL_VENDOR_DEBUG_RAMDISK_FILES := $(filter $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES)) # The debug vendor ramdisk combines vendor ramdisk and debug ramdisk. INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS := $(TARGET_VENDOR_RAMDISK_OUT) INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS := $(INTERNAL_VENDOR_RAMDISK_TARGET) # Exclude recovery files in the default vendor ramdisk if including a standalone # recovery ramdisk in vendor_boot. ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_RECOVERY_ROOT_OUT) INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) endif # BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT != true endif # BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT == true INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) # INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK would ensure TARGET_VENDOR_DEBUG_RAMDISK_OUT is created. INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json) $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS) $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL) @echo "Installed file list: $@" $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(FILESLIST) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)) INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT) $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-debug.img $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) @echo "Target debug vendor ramdisk: $@" $(copy-file-to-target) $(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) # ----------------------------------------------------------------- # vendor_boot-debug.img. INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-debug.img # The util to sign vendor_boot-debug.img with a test key. BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem INTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) # $(1): the vendor bootimage to sign define test-key-sign-vendor-bootimage $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))) $(AVBTOOL) add_hash_footer \ --image $(1) \ $(call get-partition-size-argument,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) \ --partition_name vendor_boot $(INTERNAL_AVB_VENDOR_BOOT_TEST_SIGNING_ARGS) \ $(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS) $(call assert-max-image-size,$(1),$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) endef ifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),) $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) endif # Depends on vendor_boot.img and vendor-ramdisk-debug.cpio.gz to build the new vendor_boot-debug.img $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(call pretty,"Target vendor_boot debug image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@)) $(call declare-1p-container,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE # Appends a few test harness specific properties into the adb_debug.prop. ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1 ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1 INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET := $(strip $(filter $(TARGET_DEBUG_RAMDISK_OUT)/adb_debug.prop,$(INTERNAL_DEBUG_RAMDISK_FILES))) INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET): $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) ifdef INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET $(hide) cp $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $@ endif $(hide) echo "" >> $@ $(hide) echo "#" >> $@ $(hide) echo "# ADDITIONAL TEST HARNESS PROPERTIES" >> $@ $(hide) echo "#" >> $@ $(hide) $(foreach line,$(ADDITIONAL_TEST_HARNESS_PROPERTIES), \ echo "$(line)" >> $@;) $(call declare-1p-target,$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET)) INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \ $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET) \ $(ALL_DEFAULT_INSTALLED_MODULES)) # The order is important here. The test harness ramdisk staging directory has to # come last so that it can override the adb_debug.prop in the debug ramdisk # staging directory. INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT) INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) ifdef BUILDING_DEBUG_BOOT_IMAGE # ----------------------------------------------------------------- # The test harness ramdisk, which is based off debug_ramdisk, plus a # few additional test-harness-specific properties in adb_debug.prop. INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) @echo "Target test harness ramdisk: $@" $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ $(call declare-1p-container,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) .PHONY: ramdisk_test_harness-nodeps ramdisk_test_harness-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) @echo "make $@: ignoring dependencies" $(hide) rm -f $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) $(hide) mkdir -p $(dir $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) # ----------------------------------------------------------------- # the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img # # Note: it's intentional to skip signing for boot-test-harness.img, because it # can only be used if the device is unlocked with verification error. ifneq ($(strip $(BOARD_KERNEL_BINARIES)),) INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-test-harness,$(BOARD_KERNEL_BINARIES)), \ $(PRODUCT_OUT)/$(k).img) else INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-test-harness.img endif # Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS)) # If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader # will fail to find valid AVB metadata from the end of /boot, thus stop booting. # Using a test key to sign boot-test-harness.img to continue booting with the mismatched # public key, if the device is unlocked. ifneq ($(BOARD_AVB_BOOT_KEY_PATH),) $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH) endif # $(1): output file define build-boot-test-harness-target $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-test-harness,kernel,$(notdir $(1)))) \ $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ $(BOARD_MKBOOTIMG_ARGS) --output $@ $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@,boot-test-harness)) endef # Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img. $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(AVBTOOL) $(call pretty,"Target boot test harness image: $@") $(call build-boot-test-harness-target,$@) $(call declare-1p-container,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) .PHONY: bootimage_test_harness-nodeps bootimage_test_harness-nodeps: $(MKBOOTIMG) $(AVBTOOL) echo "make $@: ignoring dependencies" $(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b)) endif # BUILDING_DEBUG_BOOT_IMAGE # ----------------------------------------------------------------- # vendor test harness ramdisk, which is a vendor ramdisk combined with # a test harness ramdisk. ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-test-harness)/vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT) # The order is important here. The test harness ramdisk staging directory has to # come last so that it can override the adb_debug.prop in the debug ramdisk # staging directory. INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT) INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS) $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS) $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) $(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@ INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-test-harness.img $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) @echo "Target test harness vendor ramdisk: $@" $(copy-file-to-target) $(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) # ----------------------------------------------------------------- # vendor_boot-test-harness.img. INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/vendor_boot-test-harness.img ifneq ($(BOARD_AVB_VENDOR_BOOT_KEY_PATH),) $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOT_TEST_KEY_PATH) endif # Depends on vendor_boot.img and vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT) to build the new vendor_boot-test-harness.img $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(call pretty,"Target vendor_boot test harness image: $@") $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_ARGS) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@)) $(call declare-1p-container,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET),$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET),$(PRODUCT_OUT)/:/) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE endif # BUILDING_DEBUG_BOOT_IMAGE || BUILDING_DEBUG_VENDOR_BOOT_IMAGE PARTITION_COMPAT_SYMLINKS := # Creates a compatibility symlink between two partitions, e.g. /system/vendor to /vendor # $1: from location (e.g $(TARGET_OUT)/vendor) # $2: destination location (e.g. /vendor) # $3: partition image name (e.g. vendor.img) define create-partition-compat-symlink $(eval \ $1: @echo Symlink $(patsubst $(PRODUCT_OUT)/%,%,$1) to $2 mkdir -p $(dir $1) if [ -d $1 ] && [ ! -h $1 ]; then \ echo 'Non-symlink $1 detected!' 1>&2; \ echo 'You cannot install files to $1 while building a separate $3!' 1>&2; \ exit 1; \ fi ln -sfn $2 $1 ) $(eval PARTITION_COMPAT_SYMLINKS += $1) $1 endef # FSVerity metadata generation # Generate fsverity metadata files (.fsv_meta) and build manifest # (/etc/security/fsverity/BuildManifest.apk) BEFORE filtering systemimage, # vendorimage, odmimage, productimage files below. ifeq ($(PRODUCT_FSVERITY_GENERATE_METADATA),true) fsverity-metadata-targets-patterns := \ $(TARGET_OUT)/framework/% \ $(TARGET_OUT)/etc/boot-image.prof \ $(TARGET_OUT)/etc/dirty-image-objects \ $(TARGET_OUT)/etc/preloaded-classes \ $(TARGET_OUT)/etc/classpaths/%.pb \ ifdef BUILDING_SYSTEM_EXT_IMAGE fsverity-metadata-targets-patterns += $(TARGET_OUT_SYSTEM_EXT)/framework/% endif # Generate fsv_meta fsverity-metadata-targets := $(sort $(filter \ $(fsverity-metadata-targets-patterns), \ $(ALL_DEFAULT_INSTALLED_MODULES))) define fsverity-generate-metadata $(call declare-0p-target,$(1).fsv_meta) $(1).fsv_meta: PRIVATE_SRC := $(1) $(1).fsv_meta: PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity $(1).fsv_meta: $(HOST_OUT_EXECUTABLES)/fsverity_metadata_generator $(HOST_OUT_EXECUTABLES)/fsverity $(1) $$< --fsverity-path $$(PRIVATE_FSVERITY) --signature none \ --hash-alg sha256 --output $$@ $$(PRIVATE_SRC) endef $(foreach f,$(fsverity-metadata-targets),$(eval $(call fsverity-generate-metadata,$(f)))) ALL_DEFAULT_INSTALLED_MODULES += $(addsuffix .fsv_meta,$(fsverity-metadata-targets)) FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) FSVERITY_APK_MANIFEST_TEMPLATE_PATH := system/security/fsverity/AndroidManifest.xml # Generate and install BuildManifest.apk for the given partition # $(1): path of the output APK # $(2): partition name define fsverity-generate-and-install-manifest-apk fsverity-metadata-targets-$(2) := $(filter $(PRODUCT_OUT)/$(2)/%,\ $(fsverity-metadata-targets)) $(1): PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity $(1): PRIVATE_AAPT2 := $(HOST_OUT_EXECUTABLES)/aapt2 $(1): PRIVATE_MIN_SDK_VERSION := $(DEFAULT_APP_TARGET_SDK) $(1): PRIVATE_VERSION_CODE := $(PLATFORM_SDK_VERSION) $(1): PRIVATE_VERSION_NAME := $(APPS_DEFAULT_VERSION_NAME) $(1): PRIVATE_APKSIGNER := $(HOST_OUT_EXECUTABLES)/apksigner $(1): PRIVATE_MANIFEST := $(FSVERITY_APK_MANIFEST_TEMPLATE_PATH) $(1): PRIVATE_FRAMEWORK_RES := $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk $(1): PRIVATE_KEY := $(FSVERITY_APK_KEY_PATH) $(1): PRIVATE_INPUTS := $$(fsverity-metadata-targets-$(2)) $(1): PRIVATE_ASSETS := $(call intermediates-dir-for,ETC,build_manifest-$(2))/assets $(1): $(HOST_OUT_EXECUTABLES)/fsverity_manifest_generator \ $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 \ $(HOST_OUT_EXECUTABLES)/apksigner $(FSVERITY_APK_MANIFEST_TEMPLATE_PATH) \ $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8 \ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk \ $$(fsverity-metadata-targets-$(2)) rm -rf $$(PRIVATE_ASSETS) mkdir -p $$(PRIVATE_ASSETS) $$< --fsverity-path $$(PRIVATE_FSVERITY) \ --base-dir $$(PRODUCT_OUT) \ --output $$(PRIVATE_ASSETS)/build_manifest.pb \ $$(PRIVATE_INPUTS) $$(PRIVATE_AAPT2) link -o $$@ \ -A $$(PRIVATE_ASSETS) \ -I $$(PRIVATE_FRAMEWORK_RES) \ --min-sdk-version $$(PRIVATE_MIN_SDK_VERSION) \ --version-code $$(PRIVATE_VERSION_CODE) \ --version-name $$(PRIVATE_VERSION_NAME) \ --manifest $$(PRIVATE_MANIFEST) \ --rename-manifest-package com.android.security.fsverity_metadata.$(2) $$(PRIVATE_APKSIGNER) sign --in $$@ \ --cert $$(PRIVATE_KEY).x509.pem \ --key $$(PRIVATE_KEY).pk8 $(1).idsig: $(1) ALL_DEFAULT_INSTALLED_MODULES += $(1) $(1).idsig endef # fsverity-generate-and-install-manifest-apk $(eval $(call fsverity-generate-and-install-manifest-apk, \ $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk,system)) ALL_FSVERITY_BUILD_MANIFEST_APK += $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk.idsig ifdef BUILDING_SYSTEM_EXT_IMAGE $(eval $(call fsverity-generate-and-install-manifest-apk, \ $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk,system_ext)) ALL_FSVERITY_BUILD_MANIFEST_APK += $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk $(TARGET_OUT_SYSTEM_EXT)/etc/security/fsverity/BuildManifestSystemExt.apk.idsig endif endif # PRODUCT_FSVERITY_GENERATE_METADATA # ----------------------------------------------------------------- # system image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_SYSTEM_IMAGE INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \ $(ALL_DEFAULT_INSTALLED_MODULES))) endif # Create symlink /system/vendor to /vendor if necessary. ifdef BOARD_USES_VENDORIMAGE _vendor_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/vendor,/vendor,vendor.img) INTERNAL_SYSTEMIMAGE_FILES += $(_vendor_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_vendor_symlink) endif # Create symlink /system/product to /product if necessary. ifdef BOARD_USES_PRODUCTIMAGE _product_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/product,/product,product.img) INTERNAL_SYSTEMIMAGE_FILES += $(_product_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_product_symlink) endif # Create symlink /system/system_ext to /system_ext if necessary. ifdef BOARD_USES_SYSTEM_EXTIMAGE _systemext_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/system_ext,/system_ext,system_ext.img) INTERNAL_SYSTEMIMAGE_FILES += $(_systemext_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_systemext_symlink) endif # ----------------------------------------------------------------- # system_dlkm partition image # Create symlinks for system_dlkm on devices with a system_dlkm partition: # /system/lib/modules -> /system_dlkm/lib/modules # # On devices with a system_dlkm partition, # - /system/lib/modules is a symlink to a directory that stores system DLKMs. # - The system_dlkm partition is mounted at /system_dlkm at runtime. ifdef BOARD_USES_SYSTEM_DLKMIMAGE _system_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT)/lib/modules,/system_dlkm/lib/modules,system_dlkm.img) INTERNAL_SYSTEMIMAGE_FILES += $(_system_dlkm_lib_modules_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_system_dlkm_lib_modules_symlink) endif FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS) # ASAN libraries in the system image - add dependency. ASAN_IN_SYSTEM_INSTALLED := $(TARGET_OUT)/asan.tar.bz2 ifneq (,$(filter address, $(SANITIZE_TARGET))) ifeq (true,$(SANITIZE_TARGET_SYSTEM)) FULL_SYSTEMIMAGE_DEPS += $(ASAN_IN_SYSTEM_INSTALLED) endif endif FULL_SYSTEMIMAGE_DEPS += $(INTERNAL_ROOT_FILES) $(INSTALLED_FILES_FILE_ROOT) # ----------------------------------------------------------------- ifdef BUILDING_SYSTEM_IMAGE # Install system linker configuration # Collect all available stub libraries installed in system and install with predefined linker configuration # Also append LLNDK libraries in the APEX as required libs SYSTEM_LINKER_CONFIG := $(TARGET_OUT)/etc/linker.config.pb SYSTEM_LINKER_CONFIG_SOURCE := system/core/rootdir/etc/linker.config.json $(SYSTEM_LINKER_CONFIG): PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE := $(SYSTEM_LINKER_CONFIG_SOURCE) $(SYSTEM_LINKER_CONFIG): $(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE) | conv_linker_config @echo Creating linker config: $@ @mkdir -p $(dir $@) @rm -f $@ $@.step1 $(HOST_OUT_EXECUTABLES)/conv_linker_config proto --force -s $(PRIVATE_SYSTEM_LINKER_CONFIG_SOURCE) -o $@.step1 $(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@.step1 \ --output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)" $(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key requireLibs \ --value "$(foreach lib,$(LLNDK_MOVED_TO_APEX_LIBRARIES), $(lib).so)" $(HOST_OUT_EXECUTABLES)/conv_linker_config append --source $@ --output $@ --key provideLibs \ --value "$(foreach lib,$(PRODUCT_EXTRA_STUB_LIBRARIES), $(lib).so)" rm -f $@.step1 $(call declare-1p-target,$(SYSTEM_LINKER_CONFIG),) $(call declare-license-deps,$(SYSTEM_LINKER_CONFIG),$(INTERNAL_SYSTEMIMAGE_FILES) $(SYSTEM_LINKER_CONFIG_SOURCE)) FULL_SYSTEMIMAGE_DEPS += $(SYSTEM_LINKER_CONFIG) ALL_DEFAULT_INSTALLED_MODULES += $(SYSTEM_LINKER_CONFIG) # installed file list # Depending on anything that $(BUILT_SYSTEMIMAGE) depends on. # We put installed-files.txt ahead of image itself in the dependency graph # so that we can get the size stat even if the build fails due to too large # system image. INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt INSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json) $(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON) $(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON))) .PHONY: installed-file-list installed-file-list: $(INSTALLED_FILES_FILE) systemimage_intermediates :=$= $(call intermediates-dir-for,PACKAGING,system) BUILT_SYSTEMIMAGE :=$= $(systemimage_intermediates)/system.img # $(1): output file define build-systemimage-target @echo "Target system fs image: $(1)" @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt $(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \ skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemimage_intermediates)/file_list.txt) \ $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \ || ( mkdir -p $${DIST_DIR}; \ cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \ exit 1 ) endef $(eval $(call write-partition-file-list,$(systemimage_intermediates)/file_list.txt,$(TARGET_OUT),$(FULL_SYSTEMIMAGE_DEPS))) ifneq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),) file_list_diff := $(HOST_OUT_EXECUTABLES)/file_list_diff$(HOST_EXECUTABLE_SUFFIX) system_file_diff_timestamp := $(systemimage_intermediates)/file_diff.timestamp # The build configuration to build the REL version may have more files to allow. # Use allowlist_next in addition to the allowlist in this case. system_file_diff_allowlist_next := ifeq (REL,$(PLATFORM_VERSION_CODENAME)) system_file_diff_allowlist_next := $(ALL_MODULES.system_image_diff_allowlist_next.INSTALLED) $(system_file_diff_timestamp): PRIVATE_ALLOWLIST_NEXT := $(system_file_diff_allowlist_next) endif $(system_file_diff_timestamp): \ $(systemimage_intermediates)/file_list.txt \ $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \ $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) \ $(system_file_diff_allowlist_next) \ $(file_list_diff) $(file_list_diff) $(systemimage_intermediates)/file_list.txt \ $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \ $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE) \ --allowlists $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) \ $(PRIVATE_ALLOWLIST_NEXT) touch $@ $(BUILT_SYSTEMIMAGE): $(system_file_diff_timestamp) endif # Used by soong sandwich to request the staging dir be built $(systemimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT)/%,$(FULL_SYSTEMIMAGE_DEPS)) touch $@ ifeq ($(BOARD_AVB_ENABLE),true) $(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH) endif ifeq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true) ifeq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),) $(error PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true) endif SOONG_DEFINED_SYSTEM_IMAGE_BASE := $(dir $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST)) $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(SOONG_DEFINED_SYSTEM_IMAGE_PATH) $(eval $(call copy-one-file, $(SOONG_DEFINED_SYSTEM_IMAGE_PATH), $(BUILT_SYSTEMIMAGE))) else $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(call build-systemimage-target,$@) endif $(call declare-1p-container,$(BUILT_SYSTEMIMAGE),system/extras) $(call declare-container-license-deps,$(BUILT_SYSTEMIMAGE),$(FULL_SYSTEMIMAGE_DEPS),$(PRODUCT_OUT)/:/) INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT) # INSTALLED_SYSTEMIMAGE_TARGET used to be named INSTALLED_SYSTEMIMAGE. Create an alias for backward # compatibility, in case device-specific Makefiles still refer to the old name. INSTALLED_SYSTEMIMAGE := $(INSTALLED_SYSTEMIMAGE_TARGET) # The system partition needs room for the recovery image as well. We # now store the recovery image as a binary patch using the boot image # as the source (since they are very similar). Generate the patch so # we can see how big it's going to be, and include that in the system # image size check calculation. ifneq ($(INSTALLED_BOOTIMAGE_TARGET),) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) ifneq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) ifneq (,$(filter true,$(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO))) diff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff else diff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff endif intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch) RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p $(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool) $(RECOVERY_FROM_BOOT_PATCH): \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) \ $(diff_tool) @echo "Construct recovery from boot" mkdir -p $(dir $@) $(PRIVATE_DIFF_TOOL) $(firstword $(INSTALLED_BOOTIMAGE_TARGET)) $(INSTALLED_RECOVERYIMAGE_TARGET) $@ else # $(BOARD_USES_FULL_RECOVERY_IMAGE) == true RECOVERY_FROM_BOOT_PATCH := $(INSTALLED_RECOVERYIMAGE_TARGET) endif # BOARD_USES_FULL_RECOVERY_IMAGE endif # INSTALLED_RECOVERYIMAGE_TARGET endif # INSTALLED_BOOTIMAGE_TARGET $(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) @echo "Install system fs image: $@" $(copy-file-to-target) $(hide) $(call assert-max-image-size,$@,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) $(call declare-1p-container,$(INSTALLED_SYSTEMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BUILT_SYSTEMIMAGE),$(BUILT_SYSTEMIMAGE):/) systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET) SYSTEM_NOTICE_DEPS += $(INSTALLED_SYSTEMIMAGE_TARGET) .PHONY: systemimage-nodeps snod systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \ | $(INTERNAL_USERIMAGES_DEPS) $(systemimage_intermediates)/file_list.txt @echo "make $@: ignoring dependencies" $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET)) $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) ifeq (true,$(WITH_DEXPREOPT)) $(warning Warning: with dexpreopt enabled, you may need a full rebuild.) endif endif # BUILDING_SYSTEM_IMAGE .PHONY: sync syncsys sync_system sync syncsys sync_system: $(INTERNAL_SYSTEMIMAGE_FILES) # ----------------------------------------------------------------- # Old PDK fusion targets .PHONY: platform platform: echo "Warning: 'platform' is obsolete" .PHONY: platform-java platform-java: echo "Warning: 'platform-java' is obsolete" # ----------------------------------------------------------------- # data partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_DATA)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_USERDATA_IMAGE INTERNAL_USERDATAIMAGE_FILES := \ $(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) userdataimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,userdata) BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img define build-userdataimage-target $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_DATA) @mkdir -p $(userdataimage_intermediates) && rm -rf $(userdataimage_intermediates)/userdata_image_info.txt $(call generate-image-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt,userdata,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(userdataimage_intermediates)/file_list.txt) \ $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt \ $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE)) endef # We just build this directly to the install location. INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET) INSTALLED_USERDATAIMAGE_TARGET_DEPS := \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_USERDATAIMAGE_FILES) $(eval $(call write-partition-file-list,$(userdataimage_intermediates)/file_list.txt,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET_DEPS))) # Used by soong sandwich to request the staging dir be built $(userdataimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_DATA)/%,$(INSTALLED_USERDATAIMAGE_TARGET_DEPS)) touch $@ $(INSTALLED_USERDATAIMAGE_TARGET): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) $(userdataimage_intermediates)/file_list.txt $(build-userdataimage-target) $(call declare-1p-container,$(INSTALLED_USERDATAIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_USERDATAIMAGE_TARGET),$(INSTALLED_USERDATAIMAGE_TARGET_DEPS),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_USERDATAIMAGE_TARGET) .PHONY: userdataimage-nodeps userdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(userdataimage_intermediates)/file_list.txt $(build-userdataimage-target) endif # BUILDING_USERDATA_IMAGE # ASAN libraries in the system image - build rule. ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL := $(sort $(patsubst $(PRODUCT_OUT)/%,%,\ $(TARGET_OUT_SHARED_LIBRARIES) \ $(2ND_TARGET_OUT_SHARED_LIBRARIES) \ $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) \ $(2ND_TARGET_OUT_VENDOR_SHARED_LIBRARIES))) # Extra options: Enforce the system user for the files to avoid having to change ownership. ASAN_SYSTEM_INSTALL_OPTIONS := --owner=1000 --group=1000 # Note: experimentally, it seems not worth it to try to get "best" compression. We don't save # enough space. $(ASAN_IN_SYSTEM_INSTALLED): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) tar cfj $(ASAN_IN_SYSTEM_INSTALLED) $(ASAN_SYSTEM_INSTALL_OPTIONS) -C $(TARGET_OUT_DATA)/.. $(ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL) >/dev/null # ----------------------------------------------------------------- # cache partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_CACHE)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_CACHE_IMAGE INTERNAL_CACHEIMAGE_FILES := \ $(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) cacheimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,cache) BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img define build-cacheimage-target $(call pretty,"Target cache fs image: $(INSTALLED_CACHEIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_CACHE) @mkdir -p $(cacheimage_intermediates) && rm -rf $(cacheimage_intermediates)/cache_image_info.txt $(call generate-image-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt,cache,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(cacheimage_intermediates)/file_list.txt) \ $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt \ $(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(cacheimage_intermediates)/file_list.txt,$(TARGET_OUT_CACHE),$(INTERNAL_CACHEIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(cacheimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_CACHE)/%,$(INTERNAL_CACHEIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET) $(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES) $(cacheimage_intermediates)/file_list.txt $(build-cacheimage-target) $(call declare-1p-container,$(INSTALLED_CACHEIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_CACHEIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_CACHEIMAGE_TARGET) .PHONY: cacheimage-nodeps cacheimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(cacheimage_intermediates)/file_list.txt $(build-cacheimage-target) else # BUILDING_CACHE_IMAGE # we need to ignore the broken cache link when doing the rsync IGNORE_CACHE_LINK := --exclude=cache endif # BUILDING_CACHE_IMAGE # ----------------------------------------------------------------- # system_other partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_OTHER)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_SYSTEM_OTHER_IMAGE ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) # Marker file to identify that odex files are installed INSTALLED_SYSTEM_OTHER_ODEX_MARKER := $(TARGET_OUT_SYSTEM_OTHER)/system-other-odex-marker ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_SYSTEM_OTHER_ODEX_MARKER) $(INSTALLED_SYSTEM_OTHER_ODEX_MARKER): $(hide) touch $@ $(call declare-0p-target,$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)) endif INTERNAL_SYSTEMOTHERIMAGE_FILES := \ $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) # system_other dex files are installed as a side-effect of installing system image files INTERNAL_SYSTEMOTHERIMAGE_FILES += $(INTERNAL_SYSTEMIMAGE_FILES) INSTALLED_FILES_FILE_SYSTEMOTHER := $(PRODUCT_OUT)/installed-files-system-other.txt INSTALLED_FILES_JSON_SYSTEMOTHER := $(INSTALLED_FILES_FILE_SYSTEMOTHER:.txt=.json) $(INSTALLED_FILES_FILE_SYSTEMOTHER): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEMOTHER) $(INSTALLED_FILES_FILE_SYSTEMOTHER) : $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_SYSTEM_OTHER) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(eval $(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEMOTHER))) $(eval $(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEMOTHER))) # Determines partition size for system_other.img. ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) ifneq ($(filter system,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),) INTERNAL_SYSTEM_OTHER_PARTITION_SIZE := $(BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE) endif endif ifndef INTERNAL_SYSTEM_OTHER_PARTITION_SIZE INTERNAL_SYSTEM_OTHER_PARTITION_SIZE:= $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) endif systemotherimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,system_other) BUILT_SYSTEMOTHERIMAGE_TARGET := $(PRODUCT_OUT)/system_other.img # Note that we assert the size is SYSTEMIMAGE_PARTITION_SIZE since this is the 'b' system image. define build-systemotherimage-target $(call pretty,"Target system_other fs image: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_SYSTEM_OTHER) @mkdir -p $(systemotherimage_intermediates) && rm -rf $(systemotherimage_intermediates)/system_other_image_info.txt $(call generate-image-prop-dictionary, $(systemotherimage_intermediates)/system_other_image_info.txt,system,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(systemotherimage_intermediates)/file_list.txt) \ $(TARGET_OUT_SYSTEM_OTHER) $(systemotherimage_intermediates)/system_other_image_info.txt \ $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(systemotherimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_OTHER),$(INTERNAL_SYSTEMOTHERIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(systemotherimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(INTERNAL_SYSTEMOTHERIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_SYSTEMOTHERIMAGE_TARGET := $(BUILT_SYSTEMOTHERIMAGE_TARGET) ifneq (true,$(SANITIZE_LITE)) # Only create system_other when not building the second stage of a SANITIZE_LITE build. $(INSTALLED_SYSTEMOTHERIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEMOTHER) $(systemotherimage_intermediates)/file_list.txt $(build-systemotherimage-target) $(call declare-1p-container,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEMOTHERIMAGE_FILES),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_DEPS += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) endif .PHONY: systemotherimage-nodeps systemotherimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(systemotherimage_intermediates)/file_list.txt $(build-systemotherimage-target) endif # BUILDING_SYSTEM_OTHER_IMAGE # ----------------------------------------------------------------- # vendor partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_VENDOR_IMAGE INTERNAL_VENDORIMAGE_FILES := \ $(filter $(TARGET_OUT_VENDOR)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) # Create symlink /vendor/odm to /odm if necessary. ifdef BOARD_USES_ODMIMAGE _odm_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/odm,/odm,odm.img) INTERNAL_VENDORIMAGE_FILES += $(_odm_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_odm_symlink) endif # Create symlinks for vendor_dlkm on devices with a vendor_dlkm partition: # /vendor/lib/modules -> /vendor_dlkm/lib/modules # # On devices with a vendor_dlkm partition, # - /vendor/lib/modules is a symlink to a directory that stores vendor DLKMs. # - /vendor_dlkm/{etc,...} store other vendor_dlkm files directly. The vendor_dlkm partition is # mounted at /vendor_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk # are hidden. # On devices without a vendor_dlkm partition, # - /vendor/lib/modules stores vendor DLKMs directly. # - /vendor_dlkm/{etc,...} are symlinks to directories that store other vendor_dlkm files. # See system/core/rootdir/Android.mk for a list of created symlinks. # The vendor DLKMs and other vendor_dlkm files must not be accessed using other paths because they # are not guaranteed to exist on all devices. ifdef BOARD_USES_VENDOR_DLKMIMAGE _vendor_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/lib/modules,/vendor_dlkm/lib/modules,vendor_dlkm.img) INTERNAL_VENDORIMAGE_FILES += $(_vendor_dlkm_lib_modules_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_vendor_dlkm_lib_modules_symlink) endif # Install vendor/etc/linker.config.pb with PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS and SOONG_STUB_VENDOR_LIBRARIES vendor_linker_config_file := $(TARGET_OUT_VENDOR)/etc/linker.config.pb $(vendor_linker_config_file): private_linker_config_fragments := $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS) $(vendor_linker_config_file): $(INTERNAL_VENDORIMAGE_FILES) $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS) | $(HOST_OUT_EXECUTABLES)/conv_linker_config @echo Creating linker config: $@ @mkdir -p $(dir $@) @rm -f $@ $(HOST_OUT_EXECUTABLES)/conv_linker_config proto \ --source $(call normalize-path-list,$(private_linker_config_fragments)) \ --output $@ $(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@ \ --output $@ --value "$(SOONG_STUB_VENDOR_LIBRARIES)" --system "$(TARGET_OUT_VENDOR)" $(call define declare-0p-target,$(vendor_linker_config_file),) INTERNAL_VENDORIMAGE_FILES += $(vendor_linker_config_file) ALL_DEFAULT_INSTALLED_MODULES += $(vendor_linker_config_file) INSTALLED_FILES_FILE_VENDOR := $(PRODUCT_OUT)/installed-files-vendor.txt INSTALLED_FILES_JSON_VENDOR := $(INSTALLED_FILES_FILE_VENDOR:.txt=.json) $(INSTALLED_FILES_FILE_VENDOR): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR) $(INSTALLED_FILES_FILE_VENDOR) : $(INTERNAL_VENDORIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_VENDOR) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR)) vendorimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,vendor) BUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img define build-vendorimage-target $(call pretty,"Target vendor fs image: $(INSTALLED_VENDORIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_VENDOR) @mkdir -p $(vendorimage_intermediates) && rm -rf $(vendorimage_intermediates)/vendor_image_info.txt $(call generate-image-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt,vendor,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendorimage_intermediates)/file_list.txt) \ $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt \ $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET) $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_VENDORIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(vendorimage_intermediates)/file_list.txt,$(TARGET_OUT_VENDOR),$(INTERNAL_VENDORIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(vendorimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR)/%,$(INTERNAL_VENDORIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET) $(INSTALLED_VENDORIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_VENDORIMAGE_FILES) \ $(INSTALLED_FILES_FILE_VENDOR) \ $(RECOVERY_FROM_BOOT_PATCH) \ $(vendorimage_intermediates)/file_list.txt $(build-vendorimage-target) VENDOR_NOTICE_DEPS += $(INSTALLED_VENDORIMAGE_TARGET) $(call declare-container-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,"Vendor Image",vendor) $(call declare-container-license-deps,$(INSTALLED_VENDORIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDORIMAGE_FILES) $(RECOVERY_FROM_BOOT_PATH),$(PRODUCT_OUT)/:/) .PHONY: vendorimage-nodeps vnod vendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS) $(vendorimage_intermediates)/file_list.txt $(build-vendorimage-target) .PHONY: sync_vendor sync sync_vendor: $(INTERNAL_VENDORIMAGE_FILES) else ifdef BOARD_PREBUILT_VENDORIMAGE INSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDORIMAGE),$(INSTALLED_VENDORIMAGE_TARGET))) $(if $(strip $(ALL_TARGETS.$(INSTALLED_VENDORIMAGE_TARGET).META_LIC)),,\ $(if $(strip $(ALL_TARGETS.$(BOARD_PREBUILT_VENDORIMAGE).META_LIC)),\ $(call declare-copy-target-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),$(BOARD_PREBUILT_VENDORIMAGE)),\ $(call declare-license-metadata,$(INSTALLED_VENDORIMAGE_TARGET),legacy_proprietary,proprietary,,"Vendor Image",vendor))) endif # ----------------------------------------------------------------- # product partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_PRODUCT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_PRODUCT_IMAGE INTERNAL_PRODUCTIMAGE_FILES := \ $(filter $(TARGET_OUT_PRODUCT)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) # Install product/etc/linker.config.pb with PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS product_linker_config_file := $(TARGET_OUT_PRODUCT)/etc/linker.config.pb $(product_linker_config_file): private_linker_config_fragments := $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS) $(product_linker_config_file): $(INTERNAL_PRODUCTIMAGE_FILES) | $(HOST_OUT_EXECUTABLES)/conv_linker_config @echo Creating linker config: $@ @mkdir -p $(dir $@) @rm -f $@ $(HOST_OUT_EXECUTABLES)/conv_linker_config proto \ --source $(call normalize-path-list,$(private_linker_config_fragments)) \ --output $@ $(call define declare-1p-target,$(product_linker_config_file),) INTERNAL_PRODUCTIMAGE_FILES += $(product_linker_config_file) ALL_DEFAULT_INSTALLED_MODULES += $(product_linker_config_file) INSTALLED_FILES_FILE_PRODUCT := $(PRODUCT_OUT)/installed-files-product.txt INSTALLED_FILES_JSON_PRODUCT := $(INSTALLED_FILES_FILE_PRODUCT:.txt=.json) $(INSTALLED_FILES_FILE_PRODUCT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_PRODUCT) $(INSTALLED_FILES_FILE_PRODUCT) : $(INTERNAL_PRODUCTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_PRODUCT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_PRODUCT)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_PRODUCT)) productimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,product) BUILT_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img define build-productimage-target $(call pretty,"Target product fs image: $(INSTALLED_PRODUCTIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_PRODUCT) @mkdir -p $(productimage_intermediates) && rm -rf $(productimage_intermediates)/product_image_info.txt $(call generate-image-prop-dictionary, $(productimage_intermediates)/product_image_info.txt,product,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(productimage_intermediates)/file_list.txt) \ $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt \ $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),$(BOARD_PRODUCTIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(productimage_intermediates)/file_list.txt,$(TARGET_OUT_PRODUCT),$(INTERNAL_PRODUCTIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(productimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_PRODUCT)/%,$(INTERNAL_PRODUCTIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_PRODUCTIMAGE_TARGET := $(BUILT_PRODUCTIMAGE_TARGET) $(INSTALLED_PRODUCTIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_PRODUCTIMAGE_FILES) \ $(INSTALLED_FILES_FILE_PRODUCT) \ $(productimage_intermediates)/file_list.txt $(build-productimage-target) PRODUCT_NOTICE_DEPS += $(INSTALLED_PRODUCTIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_PRODUCTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_PRODUCTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_PRODUCTIMAGE_FILES) $(INSTALLED_FILES_FILE_PRODUCT),$(PRODUCT_OUT)/:/) .PHONY: productimage-nodeps pnod productimage-nodeps pnod: | $(INTERNAL_USERIMAGES_DEPS) $(productimage_intermediates)/file_list.txt $(build-productimage-target) .PHONY: sync_product sync sync_product: $(INTERNAL_PRODUCTIMAGE_FILES) else ifdef BOARD_PREBUILT_PRODUCTIMAGE INSTALLED_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_PRODUCTIMAGE),$(INSTALLED_PRODUCTIMAGE_TARGET))) endif # ----------------------------------------------------------------- # system_ext partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_EXT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_SYSTEM_EXT_IMAGE INTERNAL_SYSTEM_EXTIMAGE_FILES := \ $(filter $(TARGET_OUT_SYSTEM_EXT)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_SYSTEM_EXT := $(PRODUCT_OUT)/installed-files-system_ext.txt INSTALLED_FILES_JSON_SYSTEM_EXT := $(INSTALLED_FILES_FILE_SYSTEM_EXT:.txt=.json) $(INSTALLED_FILES_FILE_SYSTEM_EXT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_EXT) $(INSTALLED_FILES_FILE_SYSTEM_EXT) : $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_SYSTEM_EXT) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_EXT)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_EXT)) system_extimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,system_ext) BUILT_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img define build-system_extimage-target $(call pretty,"Target system_ext fs image: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_SYSTEM_EXT) @mkdir -p $(system_extimage_intermediates) && rm -rf $(system_extimage_intermediates)/system_ext_image_info.txt $(call generate-image-prop-dictionary, $(system_extimage_intermediates)/system_ext_image_info.txt,system_ext, skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_extimage_intermediates)/file_list.txt) \ $(TARGET_OUT_SYSTEM_EXT) \ $(system_extimage_intermediates)/system_ext_image_info.txt \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(system_extimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_EXT),$(INTERNAL_SYSTEM_EXTIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(system_extimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(INTERNAL_SYSTEM_EXTIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_SYSTEM_EXTIMAGE_TARGET := $(BUILT_SYSTEM_EXTIMAGE_TARGET) $(INSTALLED_SYSTEM_EXTIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \ $(INSTALLED_FILES_FILE_SYSTEM_EXT) \ $(system_extimage_intermediates)/file_list.txt $(build-system_extimage-target) SYSTEM_EXT_NOTICE_DEPS += $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_SYSTEM_EXTIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_EXTIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_EXT),$(PRODUCT_OUT)/:/) .PHONY: systemextimage-nodeps senod systemextimage-nodeps senod: | $(INTERNAL_USERIMAGES_DEPS) $(system_extimage_intermediates)/file_list.txt $(build-system_extimage-target) .PHONY: sync_system_ext sync sync_system_ext: $(INTERNAL_SYSTEM_EXTIMAGE_FILES) else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE INSTALLED_SYSTEM_EXTIMAGE_TARGET := $(PRODUCT_OUT)/system_ext.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_EXTIMAGE),$(INSTALLED_SYSTEM_EXTIMAGE_TARGET))) endif # ----------------------------------------------------------------- # odm partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_ODM_IMAGE INTERNAL_ODMIMAGE_FILES := \ $(filter $(TARGET_OUT_ODM)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) # Create symlinks for odm_dlkm on devices with a odm_dlkm partition: # /odm/lib/modules -> /odm_dlkm/lib/modules # # On devices with a odm_dlkm partition, # - /odm/lib/modules is a symlink to a directory that stores odm DLKMs. # - /odm_dlkm/{etc,...} store other odm_dlkm files directly. The odm_dlkm partition is # mounted at /odm_dlkm at runtime and the symlinks created in system/core/rootdir/Android.mk # are hidden. # On devices without a odm_dlkm partition, # - /odm/lib/modules stores odm DLKMs directly. # - /odm_dlkm/{etc,...} are symlinks to directories that store other odm_dlkm files. # See system/core/rootdir/Android.mk for a list of created symlinks. # The odm DLKMs and other odm_dlkm files must not be accessed using other paths because they # are not guaranteed to exist on all devices. ifdef BOARD_USES_ODM_DLKMIMAGE _odm_dlkm_lib_modules_symlink := $(call create-partition-compat-symlink,$(TARGET_OUT_ODM)/lib/modules,/odm_dlkm/lib/modules,odm_dlkm.img) INTERNAL_ODMIMAGE_FILES += $(_odm_dlkm_lib_modules_symlink) ALL_DEFAULT_INSTALLED_MODULES += $(_odm_dlkm_lib_modules_symlink) endif INSTALLED_FILES_FILE_ODM := $(PRODUCT_OUT)/installed-files-odm.txt INSTALLED_FILES_JSON_ODM := $(INSTALLED_FILES_FILE_ODM:.txt=.json) $(INSTALLED_FILES_FILE_ODM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM) $(INSTALLED_FILES_FILE_ODM) : $(INTERNAL_ODMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_ODM) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM)) odmimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,odm) BUILT_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img define build-odmimage-target $(call pretty,"Target odm fs image: $(INSTALLED_ODMIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_ODM) @mkdir -p $(odmimage_intermediates) && rm -rf $(odmimage_intermediates)/odm_image_info.txt $(call generate-image-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, odm, \ skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odmimage_intermediates)/file_list.txt) \ $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \ $(INSTALLED_ODMIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_ODMIMAGE_TARGET),$(BOARD_ODMIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(odmimage_intermediates)/file_list.txt,$(TARGET_OUT_ODM),$(INTERNAL_ODMIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(odmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM)/%,$(INTERNAL_ODMIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_ODMIMAGE_TARGET := $(BUILT_ODMIMAGE_TARGET) $(INSTALLED_ODMIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_ODMIMAGE_FILES) \ $(INSTALLED_FILES_FILE_ODM) \ $(odmimage_intermediates)/file_list.txt $(build-odmimage-target) ODM_NOTICE_DEPS += $(INSTALLED_ODMIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_ODMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_ODMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM),$(PRODUCT_OUT)/:/) .PHONY: odmimage-nodeps onod odmimage-nodeps onod: | $(INTERNAL_USERIMAGES_DEPS) $(odmimage_intermediates)/file_list.txt $(build-odmimage-target) .PHONY: sync_odm sync sync_odm: $(INTERNAL_ODMIMAGE_FILES) else ifdef BOARD_PREBUILT_ODMIMAGE INSTALLED_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_ODMIMAGE),$(INSTALLED_ODMIMAGE_TARGET))) endif # ----------------------------------------------------------------- # vendor_dlkm partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_VENDOR_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_VENDOR_DLKM_IMAGE INTERNAL_VENDOR_DLKMIMAGE_FILES := \ $(filter $(TARGET_OUT_VENDOR_DLKM)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_VENDOR_DLKM := $(PRODUCT_OUT)/installed-files-vendor_dlkm.txt INSTALLED_FILES_JSON_VENDOR_DLKM := $(INSTALLED_FILES_FILE_VENDOR_DLKM:.txt=.json) $(INSTALLED_FILES_FILE_VENDOR_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DLKM) $(INSTALLED_FILES_FILE_VENDOR_DLKM) : $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_VENDOR_DLKM) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_VENDOR_DLKM)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_VENDOR_DLKM)) vendor_dlkmimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,vendor_dlkm) BUILT_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img define build-vendor_dlkmimage-target $(call pretty,"Target vendor_dlkm fs image: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_VENDOR_DLKM) @mkdir -p $(vendor_dlkmimage_intermediates) && rm -rf $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt $(call generate-image-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, \ vendor_dlkm, skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(vendor_dlkmimage_intermediates)/file_list.txt) \ $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \ $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(vendor_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_VENDOR_DLKM),$(INTERNAL_VENDOR_DLKMIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(vendor_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(INTERNAL_VENDOR_DLKMIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_VENDOR_DLKMIMAGE_TARGET := $(BUILT_VENDOR_DLKMIMAGE_TARGET) $(INSTALLED_VENDOR_DLKMIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \ $(INSTALLED_FILES_FILE_VENDOR_DLKM) \ $(vendor_dlkmimage_intermediates)/file_list.txt $(build-vendor_dlkmimage-target) VENDOR_DLKM_NOTICE_DEPS += $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDOR_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_VENDOR_DLKM),$(PRODUCT_OUT)/:/) .PHONY: vendor_dlkmimage-nodeps vdnod vendor_dlkmimage-nodeps vdnod: | $(INTERNAL_USERIMAGES_DEPS) $(vendor_dlkmimage_intermediates)/file_list.txt $(build-vendor_dlkmimage-target) .PHONY: sync_vendor_dlkm sync sync_vendor_dlkm: $(INTERNAL_VENDOR_DLKMIMAGE_FILES) else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE INSTALLED_VENDOR_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/vendor_dlkm.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDOR_DLKMIMAGE),$(INSTALLED_VENDOR_DLKMIMAGE_TARGET))) endif # ----------------------------------------------------------------- # odm_dlkm partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_ODM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_ODM_DLKM_IMAGE INTERNAL_ODM_DLKMIMAGE_FILES := \ $(filter $(TARGET_OUT_ODM_DLKM)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_ODM_DLKM := $(PRODUCT_OUT)/installed-files-odm_dlkm.txt INSTALLED_FILES_JSON_ODM_DLKM := $(INSTALLED_FILES_FILE_ODM_DLKM:.txt=.json) $(INSTALLED_FILES_FILE_ODM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ODM_DLKM) $(INSTALLED_FILES_FILE_ODM_DLKM) : $(INTERNAL_ODM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_ODM_DLKM) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_ODM_DLKM)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_ODM_DLKM)) odm_dlkmimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,odm_dlkm) BUILT_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img define build-odm_dlkmimage-target $(call pretty,"Target odm_dlkm fs image: $(INSTALLED_ODM_DLKMIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_ODM_DLKM) @mkdir -p $(odm_dlkmimage_intermediates) && rm -rf $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt $(call generate-image-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, \ odm_dlkm, skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(odm_dlkmimage_intermediates)/file_list.txt) \ $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \ $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(odm_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_ODM_DLKM),$(INTERNAL_ODM_DLKMIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(odm_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM_DLKM)/%,$(INTERNAL_ODM_DLKMIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_ODM_DLKMIMAGE_TARGET := $(BUILT_ODM_DLKMIMAGE_TARGET) $(INSTALLED_ODM_DLKMIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_ODM_DLKMIMAGE_FILES) \ $(INSTALLED_FILES_FILE_ODM_DLKM) \ $(odm_dlkmimage_intermediates)/file_list.txt $(build-odm_dlkmimage-target) ODM_DLKM_NOTICE_DEPS += $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_ODM_DLKMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_ODM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_ODM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_ODM_DLKM),$(PRODUCT_OUT)/:/) .PHONY: odm_dlkmimage-nodeps odnod odm_dlkmimage-nodeps odnod: | $(INTERNAL_USERIMAGES_DEPS) $(odm_dlkmimage_intermediates)/file_list.txt $(build-odm_dlkmimage-target) .PHONY: sync_odm_dlkm sync sync_odm_dlkm: $(INTERNAL_ODM_DLKMIMAGE_FILES) else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE INSTALLED_ODM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/odm_dlkm.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_ODM_DLKMIMAGE),$(INSTALLED_ODM_DLKMIMAGE_TARGET))) endif # ----------------------------------------------------------------- # system_dlkm partition image INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_SYSTEM_DLKM)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES)) ifdef BUILDING_SYSTEM_DLKM_IMAGE INTERNAL_SYSTEM_DLKMIMAGE_FILES := \ $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,\ $(ALL_DEFAULT_INSTALLED_MODULES)) INSTALLED_FILES_FILE_SYSTEM_DLKM := $(PRODUCT_OUT)/installed-files-system_dlkm.txt INSTALLED_FILES_JSON_SYSTEM_DLKM := $(INSTALLED_FILES_FILE_SYSTEM_DLKM:.txt=.json) $(INSTALLED_FILES_FILE_SYSTEM_DLKM): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_SYSTEM_DLKM) $(INSTALLED_FILES_FILE_SYSTEM_DLKM): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL) @echo Installed file list: $@ mkdir -p $(dir $@) rm -f $@ $(FILESLIST) $(TARGET_OUT_SYSTEM_DLKM) > $(@:.txt=.json) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@ $(call declare-0p-target,$(INSTALLED_FILES_FILE_SYSTEM_DLKM)) $(call declare-0p-target,$(INSTALLED_FILES_JSON_SYSTEM_DLKM)) system_dlkmimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,system_dlkm) BUILT_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img define build-system_dlkmimage-target $(call pretty,"Target system_dlkm fs image: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_SYSTEM_DLKM) @mkdir -p $(system_dlkmimage_intermediates) && rm -rf $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt $(call generate-image-prop-dictionary, $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt, \ system_dlkm, skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(if $(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES),,--input-directory-filter-file $(system_dlkmimage_intermediates)/file_list.txt) \ $(TARGET_OUT_SYSTEM_DLKM) $(system_dlkmimage_intermediates)/system_dlkm_image_info.txt \ $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(TARGET_OUT) $(call assert-max-image-size,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE)) endef $(eval $(call write-partition-file-list,$(system_dlkmimage_intermediates)/file_list.txt,$(TARGET_OUT_SYSTEM_DLKM),$(INTERNAL_SYSTEM_DLKMIMAGE_FILES))) # Used by soong sandwich to request the staging dir be built $(system_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(INTERNAL_SYSTEM_DLKMIMAGE_FILES)) touch $@ # We just build this directly to the install location. INSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(BUILT_SYSTEM_DLKMIMAGE_TARGET) $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET): \ $(INTERNAL_USERIMAGES_DEPS) \ $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \ $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \ $(system_dlkmimage_intermediates)/file_list.txt $(build-system_dlkmimage-target) SYSTEM_DLKM_NOTICE_DEPS += $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(call declare-1p-container,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) $(INSTALLED_FILES_FILE_SYSTEM_DLKM),$(PRODUCT_OUT)/:/) .PHONY: system_dlkmimage-nodeps sdnod system_dlkmimage-nodeps sdnod: | $(INTERNAL_USERIMAGES_DEPS) $(system_dlkmimage_intermediates)/file_list.txt $(build-system_dlkmimage-target) .PHONY: sync_system_dlkm sync sync_system_dlkm: $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE INSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(PRODUCT_OUT)/system_dlkm.img $(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_DLKMIMAGE),$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET))) endif # Protected VM firmware image ifeq ($(BOARD_USES_PVMFWIMAGE),true) .PHONY: pvmfwimage pvmfwimage: $(INSTALLED_PVMFWIMAGE_TARGET) INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey INSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,pvmfw_bin) INTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,pvmfw_img) INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key_pub_bin) INTERNAL_PVMFW_SYMBOL := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/pvmfw # If pvmfw target is not available and there is a prebuilt available use prebuilt # NOTE: This is only a temporary feature for x86_64 and is not meant to be supported for long. # TODO(b/391333413): Don't allow use of pvmfw prebuilts as soon as it is possible ifeq ($(INTERNAL_PVMFWIMAGE_FILES),) ifneq ($(PRODUCT_PVMFW_IMAGE_PREBUILT),) INTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,$(PRODUCT_PVMFW_IMAGE_PREBUILT)) INTERNAL_PVMFW_SYMBOL := ifneq ($(PRODUCT_PVMFW_BIN_PREBUILT),) INSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,$(PRODUCT_PVMFW_BIN_PREBUILT)) endif # PRODUCT_PVMFW_BIN_PREBUILT ifneq ($(PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT),) INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,$(PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT)) endif # PRODUCT_PVMFW_EMBEDDED_AVBKEY_PREBUILT endif # PRODUCT_PVMFW_IMAGE_PREBUILT endif # INTERNAL_PVMFWIMAGE_FILES $(call declare-1p-container,$(INSTALLED_PVMFWIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_PVMFWIMAGE_TARGET),$(INTERNAL_PVMFWIMAGE_FILES),$(PRODUCT_OUT)/:/) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_PVMFWIMAGE_TARGET) # Place the unstripped pvmfw image to the symbols directory $(INTERNAL_PVMFWIMAGE_FILES): |$(INTERNAL_PVMFW_SYMBOL) $(eval $(call copy-one-file,$(INTERNAL_PVMFWIMAGE_FILES),$(INSTALLED_PVMFWIMAGE_TARGET))) $(INSTALLED_PVMFWIMAGE_TARGET): $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) $(eval $(call copy-one-file,$(INTERNAL_PVMFW_EMBEDDED_AVBKEY),$(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET))) endif # BOARD_USES_PVMFWIMAGE # Returns a list of image targets corresponding to the given list of partitions. For example, it # returns "$(INSTALLED_PRODUCTIMAGE_TARGET)" for "product", or "$(INSTALLED_SYSTEMIMAGE_TARGET) # $(INSTALLED_VENDORIMAGE_TARGET)" for "system vendor". # (1): list of partitions like "system", "vendor" or "system product system_ext". define images-for-partitions $(strip $(foreach item,$(1),\ $(if $(filter $(item),system_other),$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),\ $(if $(filter $(item),init_boot),$(INSTALLED_INIT_BOOT_IMAGE_TARGET),\ $(INSTALLED_$(call to-upper,$(item))IMAGE_TARGET))))) endef # ----------------------------------------------------------------- # custom images INSTALLED_CUSTOMIMAGES_TARGET := ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),) INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS := BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST := # If BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH is set, the image will be included in # BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST, otherwise the image won't be AVB signed. $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ $(if $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH), \ $(eval BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST += $(partition)) \ $(eval BOARD_$(call to-upper,$(partition))_IMAGE_LIST := $(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST)))) # Sign custom image. # $(1): the prebuilt custom image. # $(2): the mount point of the prebuilt custom image. # $(3): the signed custom image target. define sign_custom_image $(3): $(1) $(INTERNAL_USERIMAGES_DEPS) @echo Target custom image: $(3) mkdir -p $(dir $(3)) cp $(1) $(3) ifeq ($(BOARD_AVB_ENABLE),true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$$$PATH \ $(AVBTOOL) add_hashtree_footer \ --image $(3) \ --key $(BOARD_AVB_$(call to-upper,$(2))_KEY_PATH) \ --algorithm $(BOARD_AVB_$(call to-upper,$(2))_ALGORITHM) \ $(call get-partition-size-argument,$(BOARD_AVB_$(call to-upper,$(2))_PARTITION_SIZE)) \ --partition_name $(2) \ $(INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS) \ $(BOARD_AVB_$(call to-upper,$(2))_ADD_HASHTREE_FOOTER_ARGS) endif INSTALLED_CUSTOMIMAGES_TARGET += $(3) endef # Copy unsigned custom image. # $(1): the prebuilt custom image. # $(2): the signed custom image target. define copy_custom_image $(2): $(1) $(INTERNAL_USERIMAGES_DEPS) @echo Target custom image: $(2) mkdir -p $(dir $(2)) cp $(1) $(2) INSTALLED_CUSTOMIMAGES_TARGET += $(2) endef # Add AVB custom image to droid target $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \ $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST), \ $(eval $(call sign_custom_image,$(image),$(partition),$(PRODUCT_OUT)/$(notdir $(image)))))) # Add unsigned custom image to droid target $(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \ $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST), \ $(eval $(call copy_custom_image,$(image),$(PRODUCT_OUT)/$(notdir $(image)))))) endif # ----------------------------------------------------------------- # vbmeta image ifeq ($(BOARD_AVB_ENABLE),true) BUILT_VBMETAIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta.img AVB_CHAIN_KEY_DIR := $(TARGET_OUT_INTERMEDIATES)/avb_chain_keys ifdef BOARD_AVB_KEY_PATH $(if $(BOARD_AVB_ALGORITHM),,$(error BOARD_AVB_ALGORITHM is not defined)) else # If key path isn't specified, use the 4096-bit test key. BOARD_AVB_ALGORITHM := SHA256_RSA4096 BOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem endif # AVB signing for system_other.img. ifdef BUILDING_SYSTEM_OTHER_IMAGE ifdef BOARD_AVB_SYSTEM_OTHER_KEY_PATH $(if $(BOARD_AVB_SYSTEM_OTHER_ALGORITHM),,$(error BOARD_AVB_SYSTEM_OTHER_ALGORITHM is not defined)) else # If key path isn't specified, use the same key as BOARD_AVB_KEY_PATH. BOARD_AVB_SYSTEM_OTHER_KEY_PATH := $(BOARD_AVB_KEY_PATH) BOARD_AVB_SYSTEM_OTHER_ALGORITHM := $(BOARD_AVB_ALGORITHM) endif $(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET): $(AVBTOOL) $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH) @echo Extracting system_other avb key: $@ @rm -f $@ @mkdir -p $(dir $@) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH) --output $@ $(eval $(call declare-0p-target,$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET),)) ifndef BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) endif BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX) endif # end of AVB for BUILDING_SYSTEM_OTHER_IMAGE INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES := \ $(BOARD_AVB_VBMETA_SYSTEM) \ $(BOARD_AVB_VBMETA_VENDOR) \ $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))) # Not allowing the same partition to appear in multiple groups. ifneq ($(words $(sort $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))),$(words $(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))) $(error BOARD_AVB_VBMETA_SYSTEM and BOARD_AVB_VBMETA_VENDOR cannot have duplicates) endif # When building a standalone recovery image for non-A/B devices, recovery image must be self-signed # to be verified independently, and cannot be chained into vbmeta.img. See the link below for # details. ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(if $(BOARD_AVB_RECOVERY_KEY_PATH),,\ $(error BOARD_AVB_RECOVERY_KEY_PATH must be defined for if non-A/B is supported. \ See https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery)) endif endif # Appends os version as a AVB property descriptor. SYSTEM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system.os_version:$(SYSTEM_OS_VERSION) PRODUCT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.product.os_version:$(PRODUCT_OS_VERSION) SYSTEM_EXT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system_ext.os_version:$(SYSTEM_EXT_OS_VERSION) INIT_BOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.init_boot.os_version:$(INIT_BOOT_OS_VERSION) BOOT_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.boot.os_version:$(BOOT_OS_VERSION) VENDOR_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor.os_version:$(VENDOR_OS_VERSION) ODM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm.os_version:$(ODM_OS_VERSION) VENDOR_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor_dlkm.os_version:$(VENDOR_DLKM_OS_VERSION) ODM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm_dlkm.os_version:$(ODM_DLKM_OS_VERSION) SYSTEM_DLKM_OS_VERSION ?= $(PLATFORM_VERSION_LAST_STABLE) BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system_dlkm.os_version:$(SYSTEM_DLKM_OS_VERSION) # Appends fingerprint and security patch level as a AVB property descriptor. BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ --prop com.android.build.system.security_patch:$(PLATFORM_SECURITY_PATCH) BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.product.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ --prop com.android.build.product.security_patch:$(PLATFORM_SECURITY_PATCH) BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system_ext.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ --prop com.android.build.system_ext.security_patch:$(PLATFORM_SECURITY_PATCH) BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.init_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.vendor_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.vendor_kernel_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \ BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.recovery.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system_dlkm.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.dtbo.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.pvmfw.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) # The following vendor- and odm-specific images needs explicit SPL set per board. # TODO(b/210875415) Is this security_patch property used? Should it be removed from # boot.img when there is no platform ramdisk included in it? ifdef BOOT_SECURITY_PATCH BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.boot.security_patch:$(BOOT_SECURITY_PATCH) endif ifdef INIT_BOOT_SECURITY_PATCH BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.init_boot.security_patch:$(INIT_BOOT_SECURITY_PATCH) else ifdef BOOT_SECURITY_PATCH BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.init_boot.security_patch:$(BOOT_SECURITY_PATCH) endif ifdef VENDOR_SECURITY_PATCH BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor.security_patch:$(VENDOR_SECURITY_PATCH) endif ifdef ODM_SECURITY_PATCH BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm.security_patch:$(ODM_SECURITY_PATCH) endif ifdef VENDOR_DLKM_SECURITY_PATCH BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.vendor_dlkm.security_patch:$(VENDOR_DLKM_SECURITY_PATCH) endif ifdef ODM_DLKM_SECURITY_PATCH BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.odm_dlkm.security_patch:$(ODM_DLKM_SECURITY_PATCH) endif ifdef SYSTEM_DLKM_SECURITY_PATCH BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += \ --prop com.android.build.system_dlkm.security_patch:$(SYSTEM_DLKM_SECURITY_PATCH) endif ifdef PVMFW_SECURITY_PATCH BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH) endif # Append root digest of microdroid-vendor partition's hashtree descriptor into vendor partition. ifdef MICRODROID_VENDOR_IMAGE_MODULE MICRODROID_VENDOR_IMAGE := \ $(call intermediates-dir-for,ETC,$(MICRODROID_VENDOR_IMAGE_MODULE))/$(MICRODROID_VENDOR_IMAGE_MODULE) MICRODROID_VENDOR_ROOT_DIGEST := $(PRODUCT_OUT)/microdroid_vendor_root_digest BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \ --prop_from_file com.android.build.microdroid-vendor.root_digest:$(MICRODROID_VENDOR_ROOT_DIGEST) $(MICRODROID_VENDOR_ROOT_DIGEST): $(AVBTOOL) $(MICRODROID_VENDOR_IMAGE) $(AVBTOOL) print_partition_digests \ --image $(MICRODROID_VENDOR_IMAGE) \ | tr -d '\n' | sed -E 's/.*: //g' > $@ $(INSTALLED_VENDORIMAGE_TARGET): $(MICRODROID_VENDOR_ROOT_DIGEST) endif BOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS INIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS VENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS VENDOR_KERNEL_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS DTBO_FOOTER_ARGS := BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS PVMFW_FOOTER_ARGS := BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS SYSTEM_FOOTER_ARGS := BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS VENDOR_FOOTER_ARGS := BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS RECOVERY_FOOTER_ARGS := BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS PRODUCT_FOOTER_ARGS := BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS SYSTEM_EXT_FOOTER_ARGS := BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS ODM_FOOTER_ARGS := BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS VENDOR_DLKM_FOOTER_ARGS := BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS ODM_DLKM_FOOTER_ARGS := BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS SYSTEM_DLKM_FOOTER_ARGS := BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS # Helper function that checks and sets required build variables for an AVB chained partition. # $(1): the partition to enable AVB chain, e.g., boot or system or vbmeta_system. define _check-and-set-avb-chain-args $(eval part := $(1)) $(eval PART=$(call to-upper,$(part))) $(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH) $(eval _signing_algorithm := BOARD_AVB_$(PART)_ALGORITHM) $(eval _rollback_index := BOARD_AVB_$(PART)_ROLLBACK_INDEX) $(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION) $(if $($(_key_path)),,$(error $(_key_path) is not defined)) $(if $($(_signing_algorithm)),,$(error $(_signing_algorithm) is not defined)) $(if $($(_rollback_index)),,$(error $(_rollback_index) is not defined)) $(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined)) # Set INTERNAL_AVB_(PART)_SIGNING_ARGS $(eval _signing_args := INTERNAL_AVB_$(PART)_SIGNING_ARGS) $(eval $(_signing_args) := \ --algorithm $($(_signing_algorithm)) --key $($(_key_path))) # The recovery partition in non-A/B devices should be verified separately. Skip adding the chain # partition descriptor for recovery partition into vbmeta.img. $(if $(or $(filter-out true,$(TARGET_OTA_ALLOW_NON_AB)),$(filter-out recovery,$(part))),\ $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey)) # Set rollback_index via footer args for non-chained vbmeta image. Chained vbmeta image will pick up # the index via a separate flag (e.g. BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX). $(if $(filter $(part),$(part:vbmeta_%=%)),\ $(eval _footer_args := $(PART)_FOOTER_ARGS) \ $(eval $($(_footer_args)) += --rollback_index $($(_rollback_index)))) endef # Checks and sets the required build variables for an AVB partition. The partition will be # configured as a chained partition, if BOARD_AVB__KEY_PATH is defined. Otherwise the # image descriptor will be included into vbmeta.img, unless it has been already added to any chained # VBMeta image. # Multiple boot images can be generated based on BOARD_KERNEL_BINARIES # but vbmeta would capture the image descriptor of only the first boot # image specified in BUILT_BOOTIMAGE_TARGET. # $(1): Partition name, e.g. boot or system. define check-and-set-avb-args $(eval _in_chained_vbmeta := $(filter $(1),$(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES))) $(if $(BOARD_AVB_$(call to-upper,$(1))_KEY_PATH),\ $(if $(_in_chained_vbmeta),\ $(error Chaining partition "$(1)" in chained VBMeta image is not supported)) \ $(call _check-and-set-avb-chain-args,$(1)),\ $(if $(_in_chained_vbmeta),,\ $(if $(filter boot,$(1)),\ $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ --include_descriptors_from_image $(firstword $(call images-for-partitions,$(1)))),\ $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ --include_descriptors_from_image $(call images-for-partitions,$(1)))))) endef # Checks and sets build variables for a custom chained partition to include it into vbmeta.img. # $(1): the custom partition to enable AVB chain. define check-and-set-custom-avb-chain-args $(eval part := $(1)) $(eval PART=$(call to-upper,$(part))) $(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION) $(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH) $(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined)) $(if $($(_key_path)),,$(error $(_key_path) is not defined)) INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \ --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey endef ifdef INSTALLED_BOOTIMAGE_TARGET $(eval $(call check-and-set-avb-args,boot)) endif ifdef INSTALLED_INIT_BOOT_IMAGE_TARGET $(eval $(call check-and-set-avb-args,init_boot)) endif ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET $(eval $(call check-and-set-avb-args,vendor_boot)) endif ifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET $(eval $(call check-and-set-avb-args,vendor_kernel_boot)) endif ifdef INSTALLED_SYSTEMIMAGE_TARGET $(eval $(call check-and-set-avb-args,system)) endif ifdef INSTALLED_VENDORIMAGE_TARGET $(eval $(call check-and-set-avb-args,vendor)) endif ifdef INSTALLED_PRODUCTIMAGE_TARGET $(eval $(call check-and-set-avb-args,product)) endif ifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET $(eval $(call check-and-set-avb-args,system_ext)) endif ifdef INSTALLED_ODMIMAGE_TARGET $(eval $(call check-and-set-avb-args,odm)) endif ifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET $(eval $(call check-and-set-avb-args,vendor_dlkm)) endif ifdef INSTALLED_ODM_DLKMIMAGE_TARGET $(eval $(call check-and-set-avb-args,odm_dlkm)) endif ifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET $(eval $(call check-and-set-avb-args,system_dlkm)) endif ifdef INSTALLED_DTBOIMAGE_TARGET $(eval $(call check-and-set-avb-args,dtbo)) endif ifdef INSTALLED_PVMFWIMAGE_TARGET $(eval $(call check-and-set-avb-args,pvmfw)) endif ifdef INSTALLED_RECOVERYIMAGE_TARGET $(eval $(call check-and-set-avb-args,recovery)) endif # Not using INSTALLED_VBMETA_SYSTEMIMAGE_TARGET as it won't be set yet. ifdef BOARD_AVB_VBMETA_SYSTEM $(eval $(call check-and-set-avb-args,vbmeta_system)) endif ifdef BOARD_AVB_VBMETA_VENDOR $(eval $(call check-and-set-avb-args,vbmeta_vendor)) endif ifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(eval $(call check-and-set-avb-args,vbmeta_$(partition)))) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(eval BOARD_AVB_MAKE_VBMETA_$(call to-upper,$(partition))_IMAGE_ARGS += --padding_size 4096)) endif ifneq ($(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)),) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \ $(eval $(call check-and-set-custom-avb-chain-args,$(partition)))) endif BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --padding_size 4096 BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += --padding_size 4096 BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += --padding_size 4096 ifeq (eng,$(filter eng, $(TARGET_BUILD_VARIANT))) # We only need the flag in top-level vbmeta.img. BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --set_hashtree_disabled_flag endif ifdef BOARD_AVB_ROLLBACK_INDEX BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --rollback_index $(BOARD_AVB_ROLLBACK_INDEX) endif ifdef BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += \ --rollback_index $(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX) endif ifdef BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += \ --rollback_index $(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX) endif ifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)), \ $(if $(BOARD_AVB_VBMETA_$(partition)_ROLLBACK_INDEX),$(eval \ BOARD_AVB_MAKE_VBMETA_$(partition)_IMAGE_ARGS += \ --rollback_index $(BOARD_AVB_VBMETA_$(partition)_ROLLBACK_INDEX)))) endif # $(1): the directory to extract public keys to define extract-avb-chain-public-keys $(if $(BOARD_AVB_BOOT_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_BOOT_KEY_PATH) \ --output $(1)/boot.avbpubkey) $(if $(BOARD_AVB_INIT_BOOT_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_INIT_BOOT_KEY_PATH) \ --output $(1)/init_boot.avbpubkey) $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),\ $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_BOOT_KEY_PATH) \ --output $(1)/vendor_boot.avbpubkey) $(if $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH),\ $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH) \ --output $(1)/vendor_kernel_boot.avbpubkey) $(if $(BOARD_AVB_SYSTEM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_KEY_PATH) \ --output $(1)/system.avbpubkey) $(if $(BOARD_AVB_VENDOR_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_KEY_PATH) \ --output $(1)/vendor.avbpubkey) $(if $(BOARD_AVB_PRODUCT_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PRODUCT_KEY_PATH) \ --output $(1)/product.avbpubkey) $(if $(BOARD_AVB_SYSTEM_EXT_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_EXT_KEY_PATH) \ --output $(1)/system_ext.avbpubkey) $(if $(BOARD_AVB_ODM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_KEY_PATH) \ --output $(1)/odm.avbpubkey) $(if $(BOARD_AVB_VENDOR_DLKM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_DLKM_KEY_PATH) \ --output $(1)/vendor_dlkm.avbpubkey) $(if $(BOARD_AVB_ODM_DLKM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_ODM_DLKM_KEY_PATH) \ --output $(1)/odm_dlkm.avbpubkey) $(if $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_SYSTEM_DLKM_KEY_PATH) \ --output $(1)/system_dlkm.avbpubkey) $(if $(BOARD_AVB_DTBO_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_DTBO_KEY_PATH) \ --output $(1)/dtbo.avbpubkey) $(if $(BOARD_AVB_PVMFW_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_PVMFW_KEY_PATH) \ --output $(1)/pvmfw.avbpubkey) $(if $(BOARD_AVB_RECOVERY_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_RECOVERY_KEY_PATH) \ --output $(1)/recovery.avbpubkey) $(if $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \ --output $(1)/vbmeta_system.avbpubkey) $(if $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH),\ $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \ --output $(1)/vbmeta_vendor.avbpubkey) $(if $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST),\ $(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \ $(AVBTOOL) extract_public_key --key $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH) \ --output $(1)/$(partition).avbpubkey;)) $(if $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\ $(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \ $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH) \ --output $(1)/vbmeta_$(partition).avbpubkey;)) endef # Builds a chained VBMeta image. This VBMeta image will contain the descriptors for the partitions # specified in BOARD_AVB_VBMETA_. The built VBMeta image will be included into the top-level # vbmeta image as a chained partition. For example, if a target defines `BOARD_AVB_VBMETA_SYSTEM # := system system_ext`, `vbmeta_system.img` will be created that includes the descriptors for # `system.img` and `system_ext.img`. `vbmeta_system.img` itself will be included into # `vbmeta.img` as a chained partition. # $(1): VBMeta image name, such as "vbmeta_system", "vbmeta_vendor" etc. # $(2): Output filename. define build-chained-vbmeta-image $(call pretty,"Target chained vbmeta image: $@") $(hide) $(AVBTOOL) make_vbmeta_image \ $(INTERNAL_AVB_$(call to-upper,$(1))_SIGNING_ARGS) \ $(BOARD_AVB_MAKE_$(call to-upper,$(1))_IMAGE_ARGS) \ $(foreach image,$(BOARD_AVB_$(call to-upper,$(1))), \ --include_descriptors_from_image $(call images-for-partitions,$(image))) \ --output $@ # libavb expects to be able to read the maximum vbmeta size, so we must provide a partition # which matches this or the read will fail. # See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE truncate -s 65536 $@ endef ifdef BUILDING_SYSTEM_IMAGE ifdef BOARD_AVB_VBMETA_SYSTEM INSTALLED_VBMETA_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_system.img $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET): \ $(AVBTOOL) \ $(call images-for-partitions,$(BOARD_AVB_VBMETA_SYSTEM)) \ $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) $(call build-chained-vbmeta-image,vbmeta_system) $(call declare-1p-container,$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET),) SYSTEM_NOTICE_DEPS += $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) endif endif # BUILDING_SYSTEM_IMAGE ifdef BOARD_AVB_VBMETA_VENDOR INSTALLED_VBMETA_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_vendor.img $(INSTALLED_VBMETA_VENDORIMAGE_TARGET): \ $(AVBTOOL) \ $(call images-for-partitions,$(BOARD_AVB_VBMETA_VENDOR)) \ $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) $(call build-chained-vbmeta-image,vbmeta_vendor) $(call declare-1p-container,$(INSTALLED_VBMETA_VENDORIMAGE_TARGET),) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) endif ifdef BOARD_AVB_VBMETA_CUSTOM_PARTITIONS define declare-custom-vbmeta-target INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET := $(PRODUCT_OUT)/vbmeta_$(call to-lower,$(1)).img $$(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET): \ $(AVBTOOL) \ $(call images-for-partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(1)))) \ $(BOARD_AVB_VBMETA_$(call to-upper,$(1))_KEY_PATH) $$(call build-chained-vbmeta-image,vbmeta_$(call to-lower,$(1))) $(call declare-1p-container,$(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET),) UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_VBMETA_$(call to-upper,$(1))IMAGE_TARGET) endef $(foreach partition,\ $(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),\ $(eval $(call declare-custom-vbmeta-target,$(partition)))) endif define build-vbmetaimage-target $(call pretty,"Target vbmeta image: $(INSTALLED_VBMETAIMAGE_TARGET)") $(hide) mkdir -p $(AVB_CHAIN_KEY_DIR) $(call extract-avb-chain-public-keys, $(AVB_CHAIN_KEY_DIR)) $(hide) $(AVBTOOL) make_vbmeta_image \ $(INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS) \ $(PRIVATE_AVB_VBMETA_SIGNING_ARGS) \ $(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS) \ --output $@ # libavb expects to be able to read the maximum vbmeta size, so we must provide a partition # which matches this or the read will fail. # See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE truncate -s 65536 $@ $(hide) rm -rf $(AVB_CHAIN_KEY_DIR) endef ifdef BUILDING_VBMETA_IMAGE INSTALLED_VBMETAIMAGE_TARGET := $(BUILT_VBMETAIMAGE_TARGET) $(INSTALLED_VBMETAIMAGE_TARGET): PRIVATE_AVB_VBMETA_SIGNING_ARGS := \ --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH) $(INSTALLED_VBMETAIMAGE_TARGET): \ $(AVBTOOL) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \ $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ $(INSTALLED_DTBOIMAGE_TARGET) \ $(INSTALLED_PVMFWIMAGE_TARGET) \ $(INSTALLED_CUSTOMIMAGES_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \ $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \ $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET)) \ $(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH) \ $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \ $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(BOARD_AVB_VBMETA_$(partition)_KEY_PATH)) \ $(BOARD_AVB_KEY_PATH) $(build-vbmetaimage-target) $(call declare-1p-container,$(INSTALLED_VBMETAIMAGE_TARGET),) UNMOUNTED_NOTICE_DEPS += $(INSTALLED_VBMETAIMAGE_TARGET) .PHONY: vbmetaimage-nodeps vbmetaimage-nodeps: PRIVATE_AVB_VBMETA_SIGNING_ARGS := \ --algorithm $(BOARD_AVB_ALGORITHM) --key $(BOARD_AVB_KEY_PATH) vbmetaimage-nodeps: $(build-vbmetaimage-target) endif # BUILDING_VBMETA_IMAGE endif # BOARD_AVB_ENABLE # List of files from all images INTERNAL_ALLIMAGES_FILES := \ $(FULL_SYSTEMIMAGE_DEPS) \ $(INTERNAL_RAMDISK_FILES) \ $(INTERNAL_USERDATAIMAGE_FILES) \ $(INTERNAL_VENDORIMAGE_FILES) \ $(INTERNAL_PRODUCTIMAGE_FILES) \ $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \ $(INTERNAL_ODMIMAGE_FILES) \ $(INTERNAL_VENDOR_DLKMIMAGE_FILES) \ $(INTERNAL_ODM_DLKMIMAGE_FILES) \ $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) \ $(INTERNAL_PVMFWIMAGE_FILES) \ # ----------------------------------------------------------------- # Run apex_sepolicy_tests for all installed APEXes ifeq (,$(TARGET_BUILD_UNBUNDLED)) ifneq (,$(filter ext4 erofs,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE))) intermediate := $(call intermediates-dir-for,PACKAGING,apex_sepolicy_tests) apex_dirs := \ $(TARGET_OUT)/apex/% \ $(TARGET_OUT_SYSTEM_EXT)/apex/% \ $(TARGET_OUT_VENDOR)/apex/% \ $(TARGET_OUT_ODM)/apex/% \ $(TARGET_OUT_PRODUCT)/apex/% \ apex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES))) apex_dirs := # $1: apex file # $2: output file define _run_apex_sepolicy_tests $2: $1 \ $(HOST_OUT_EXECUTABLES)/apex_sepolicy_tests \ $(HOST_OUT_EXECUTABLES)/apex-ls @rm -rf $$@ @mkdir -p $(dir $$@) $(HOST_OUT_EXECUTABLES)/apex_sepolicy_tests --all -f <($(HOST_OUT_EXECUTABLES)/apex-ls -Z $$<) @touch $$@ endef # $1: apex file list define run_apex_sepolicy_tests $(foreach apex_file,$1, \ $(eval passfile := $(patsubst $(PRODUCT_OUT)/%,$(intermediate)/%.pass,$(apex_file))) \ $(eval $(call _run_apex_sepolicy_tests,$(apex_file),$(passfile))) \ $(passfile)) endef .PHONY: run_apex_sepolicy_tests run_apex_sepolicy_tests: $(call run_apex_sepolicy_tests,$(apex_files)) droid_targets: run_apex_sepolicy_tests apex_files := intermediate := endif # PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE endif # TARGET_BUILD_UNBUNDLED # ----------------------------------------------------------------- # Check VINTF of build # Note: vendor_dlkm, odm_dlkm, and system_dlkm does not have VINTF files. ifeq (,$(TARGET_BUILD_UNBUNDLED)) intermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all) check_vintf_all_deps := # ----------------------------------------------------------------- # Activate APEXes for checkvintf apex_dirs := \ $(TARGET_OUT)/apex/% \ $(TARGET_OUT_PRODUCT)/apex/% \ $(TARGET_OUT_SYSTEM_EXT)/apex/% \ $(TARGET_OUT_VENDOR)/apex/% \ $(TARGET_OUT_ODM)/apex/% \ apex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES))) APEX_OUT := $(intermediates)/apex APEX_INFO_FILE := $(APEX_OUT)/apex-info-list.xml # apexd_host scans/activates APEX files and writes /apex/apex-info-list.xml # Note that `@echo $(PRIVATE_APEX_FILES)` line is added to trigger the rule when the APEX list is changed. $(APEX_INFO_FILE): PRIVATE_APEX_FILES := $(apex_files) $(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/apexd_host \ $(HOST_OUT_EXECUTABLES)/deapexer $(HOST_OUT_EXECUTABLES)/debugfs $(HOST_OUT_EXECUTABLES)/fsck.erofs \ $(apex_files) @echo "Extracting apexes..." @echo $(PRIVATE_APEX_FILES) > /dev/null @rm -rf $(APEX_OUT) @mkdir -p $(APEX_OUT) $< --system_path $(TARGET_OUT) \ --system_ext_path $(TARGET_OUT_SYSTEM_EXT) \ --product_path $(TARGET_OUT_PRODUCT) \ --vendor_path $(TARGET_OUT_VENDOR) \ --odm_path $(TARGET_OUT_ODM) \ --apex_path $(APEX_OUT) apex_files := apex_dirs := # The build system only writes VINTF metadata to */etc/vintf paths. Legacy paths aren't needed here # because they are only used for prebuilt images. # APEX files in /$partition/apex can have VINTF fragments as well. check_vintf_common_srcs_patterns := \ $(TARGET_OUT)/etc/vintf/% \ $(TARGET_OUT_VENDOR)/etc/vintf/% \ $(TARGET_OUT_ODM)/etc/vintf/% \ $(TARGET_OUT_PRODUCT)/etc/vintf/% \ $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \ $(apex_dirs) check_vintf_common_srcs := $(sort $(filter $(check_vintf_common_srcs_patterns),$(INTERNAL_ALLIMAGES_FILES))) check_vintf_common_srcs_patterns := check_vintf_has_system := check_vintf_has_vendor := ifneq (,$(filter EMPTY_ODM_SKU_PLACEHOLDER,$(ODM_MANIFEST_SKUS))) $(error EMPTY_ODM_SKU_PLACEHOLDER is an internal variable and cannot be used for ODM_MANIFEST_SKUS) endif ifneq (,$(filter EMPTY_VENDOR_SKU_PLACEHOLDER,$(DEVICE_MANIFEST_SKUS))) $(error EMPTY_VENDOR_SKU_PLACEHOLDER is an internal variable and cannot be used for DEIVCE_MANIFEST_SKUS) endif # -- Check system and system_ext manifests / matrices including fragments (excluding other framework manifests / matrices, e.g. product); ifdef BUILDING_SYSTEM_IMAGE check_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/% \ $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \ $(TARGET_OUT)/apex/% \ $(TARGET_OUT_SYSTEM_EXT)/apex/%, \ $(check_vintf_common_srcs)) ifneq ($(check_vintf_system_deps),) check_vintf_has_system := true check_vintf_system_log := $(intermediates)/check_vintf_system.log check_vintf_all_deps += $(check_vintf_system_log) $(check_vintf_system_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_system_deps) $(APEX_INFO_FILE) @( $< --check-one --dirmap /system:$(TARGET_OUT) --dirmap /apex:$(APEX_OUT) > $@ 2>&1 ) || ( cat $@ && exit 1 ) $(call declare-1p-target,$(check_vintf_system_log)) check_vintf_system_log := # -- Check framework manifest against frozen manifests for GSI targets. They need to be compatible. ifneq (true, $(BUILDING_VENDOR_IMAGE)) vintffm_log := $(intermediates)/vintffm.log endif check_vintf_all_deps += $(vintffm_log) $(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps) $(APEX_INFO_FILE) @( $< --check --dirmap /system:$(TARGET_OUT) \ --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \ --dirmap /product:$(TARGET_OUT_PRODUCT) \ --dirmap /apex:$(APEX_OUT) \ system/libhidl/vintfdata/frozen > $@ 2>&1 ) || ( cat $@ && exit 1 ) $(call declare-1p-target,$(vintffm_log)) endif # check_vintf_system_deps check_vintf_system_deps := endif # BUILDING_SYSTEM_IMAGE # -- Check vendor manifest / matrix including fragments (excluding other device manifests / matrices) check_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/% \ $(TARGET_OUT_VENDOR)/apex/%, \ $(check_vintf_common_srcs)) ifneq ($(strip $(check_vintf_vendor_deps)),) check_vintf_has_vendor := true check_vintf_vendor_log := $(intermediates)/check_vintf_vendor.log check_vintf_all_deps += $(check_vintf_vendor_log) # Check vendor SKU=(empty) case when: # - DEVICE_MANIFEST_FILE is not empty; OR # - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used) $(check_vintf_vendor_log): PRIVATE_VENDOR_SKUS := \ $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\ $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \ $(DEVICE_MANIFEST_SKUS) $(check_vintf_vendor_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_vendor_deps) $(APEX_INFO_FILE) $(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \ ( $< --check-one --dirmap /vendor:$(TARGET_OUT_VENDOR) --dirmap /apex:$(APEX_OUT) \ --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \ > $@ 2>&1 ) || ( cat $@ && exit 1 ); ) $(call declare-1p-target,$(check_vintf_vendor_log)) check_vintf_vendor_log := endif # check_vintf_vendor_deps check_vintf_vendor_deps := # -- Kernel version and configurations. ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true) BUILT_KERNEL_CONFIGS_FILE := $(intermediates)/kernel_configs.txt BUILT_KERNEL_VERSION_FILE := $(intermediates)/kernel_version.txt my_board_extracted_kernel := # Tools for decompression that is not in PATH. # Check $(EXTRACT_KERNEL) for decompression algorithms supported by the script. # Algorithms that are in the script but not in this list will be found in PATH. my_decompress_tools := \ lz4:$(HOST_OUT_EXECUTABLES)/lz4 \ # BOARD_KERNEL_VERSION can be used to override the values extracted # from INSTALLED_KERNEL_TARGET. ifdef BOARD_KERNEL_VERSION $(BUILT_KERNEL_VERSION_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools) $(BUILT_KERNEL_VERSION_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair))) $(BUILT_KERNEL_VERSION_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET)) KERNEL_RELEASE=`$(EXTRACT_KERNEL) --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \ --output-release` ;\ if [ "$$KERNEL_RELEASE" != '$(BOARD_KERNEL_VERSION)' ]; then \ echo "Specified kernel version '$(BOARD_KERNEL_VERSION)' does not match actual kernel version '$$KERNEL_RELEASE' " ; exit 1; fi; echo '$(BOARD_KERNEL_VERSION)' > $@ $(call declare-license-metadata,$(BUILT_KERNEL_VERSION_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel) endif # BOARD_KERNEL_VERSION ifneq ($(my_board_extracted_kernel),true) ifdef INSTALLED_KERNEL_TARGET ifndef BOARD_KERNEL_VERSION $(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE) endif $(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools) $(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair))) $(BUILT_KERNEL_CONFIGS_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET)) $< --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \ --output-configs $@ \ $(if $(BOARD_KERNEL_VERSION),,--output-release $(BUILT_KERNEL_VERSION_FILE)) $(call declare-license-metadata,$(BUILT_KERNEL_CONFIGS_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel) my_board_extracted_kernel := true endif # INSTALLED_KERNEL_TARGET endif # my_board_extracted_kernel ifneq ($(my_board_extracted_kernel),true) ifdef INSTALLED_BOOTIMAGE_TARGET $(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE) $(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools) $(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair))) $(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_UNPACKED_BOOTIMG := $(intermediates)/unpacked_bootimage $(BUILT_KERNEL_CONFIGS_FILE): \ $(HOST_OUT_EXECUTABLES)/unpack_bootimg \ $(EXTRACT_KERNEL) \ $(INSTALLED_BOOTIMAGE_TARGET) $(HOST_OUT_EXECUTABLES)/unpack_bootimg --boot_img $(INSTALLED_BOOTIMAGE_TARGET) --out $(PRIVATE_UNPACKED_BOOTIMG) $(EXTRACT_KERNEL) --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(PRIVATE_UNPACKED_BOOTIMG)/kernel \ --output-configs $@ \ --output-release $(BUILT_KERNEL_VERSION_FILE) $(call declare-license-metadata,$(BUILT_KERNEL_CONFIGS_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel) my_board_extracted_kernel := true endif # INSTALLED_BOOTIMAGE_TARGET endif # my_board_extracted_kernel ifeq ($(my_board_extracted_kernel),true) $(call dist-for-goals, droid_targets, $(BUILT_KERNEL_VERSION_FILE)) else $(warning Neither INSTALLED_KERNEL_TARGET nor INSTALLED_BOOTIMAGE_TARGET is defined when \ PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS is true. Information about the updated kernel \ cannot be built into OTA update package. You can fix this by: \ (1) setting TARGET_NO_KERNEL to false and installing the built kernel to $(PRODUCT_OUT)/kernel,\ so that kernel information will be extracted from the built kernel; or \ (2) Add a prebuilt boot image and specify it in BOARD_PREBUILT_BOOTIMAGE; or \ (3) extracting kernel configuration and defining BOARD_KERNEL_CONFIG_FILE and \ BOARD_KERNEL_VERSION manually; or \ (4) unsetting PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS manually.) # Clear their values to indicate that these two files does not exist. BUILT_KERNEL_CONFIGS_FILE := BUILT_KERNEL_VERSION_FILE := endif my_decompress_tools := my_board_extracted_kernel := endif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS ifeq (default,$(ENABLE_UFFD_GC)) ifneq (,$(BUILT_KERNEL_VERSION_FILE)) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC): $(BUILT_KERNEL_VERSION_FILE) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC): if ! cmp -s $(BUILT_KERNEL_VERSION_FILE) $@ ; then cp $(BUILT_KERNEL_VERSION_FILE) $@; fi .KATI_RESTAT: $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC) else # We make this a warning rather than an error to avoid breaking too many builds. When it happens, # we use a placeholder as the kernel version, which is consumed by uffd_gc_utils.py. $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC): echo $$'\ Unable to determine UFFD GC flag because the kernel version is not available and\n\ PRODUCT_ENABLE_UFFD_GC is "default".\n\ You can fix this by:\n\ 1. [Recommended] Making the kernel version available.\n\ (1). Set PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS to "true".\n\ (2). If you are still getting this message after doing so, see the warning about\n\ PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS in the build logs.\n\ or\n\ 2. Explicitly setting PRODUCT_ENABLE_UFFD_GC to "true" or "false" based on the kernel version.\n\ (1). Set PRODUCT_ENABLE_UFFD_GC to "true" if the kernel is a GKI kernel and is android12-5.4\n\ or above, or a non-GKI kernel that supports userfaultfd(2) and MREMAP_DONTUNMAP.\n\ (2). Set PRODUCT_ENABLE_UFFD_GC to "false" otherwise.'\ && echo '' > $@ endif # BUILT_KERNEL_VERSION_FILE endif # ENABLE_UFFD_GC == "default" # -- Check VINTF compatibility of build. # Skip partial builds; only check full builds. Only check if: # - PRODUCT_ENFORCE_VINTF_MANIFEST is true # - system / vendor VINTF metadata exists # - Building product / system_ext / odm images if board has product / system_ext / odm images ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true) ifeq ($(check_vintf_has_system),true) ifeq ($(check_vintf_has_vendor),true) ifeq ($(filter true,$(BUILDING_ODM_IMAGE)),$(filter true,$(BOARD_USES_ODMIMAGE))) ifeq ($(filter true,$(BUILDING_PRODUCT_IMAGE)),$(filter true,$(BOARD_USES_PRODUCTIMAGE))) ifeq ($(filter true,$(BUILDING_SYSTEM_EXT_IMAGE)),$(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE))) check_vintf_compatible_log := $(intermediates)/check_vintf_compatible.log check_vintf_all_deps += $(check_vintf_compatible_log) check_vintf_compatible_args := check_vintf_compatible_deps := $(check_vintf_common_srcs) $(APEX_INFO_FILE) ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true) ifneq (,$(BUILT_KERNEL_VERSION_FILE)$(BUILT_KERNEL_CONFIGS_FILE)) check_vintf_compatible_args += --kernel $(BUILT_KERNEL_VERSION_FILE):$(BUILT_KERNEL_CONFIGS_FILE) check_vintf_compatible_deps += $(BUILT_KERNEL_CONFIGS_FILE) $(BUILT_KERNEL_VERSION_FILE) endif # BUILT_KERNEL_VERSION_FILE != "" || BUILT_KERNEL_CONFIGS_FILE != "" endif # PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS check_vintf_compatible_args += \ --dirmap /system:$(TARGET_OUT) \ --dirmap /vendor:$(TARGET_OUT_VENDOR) \ --dirmap /odm:$(TARGET_OUT_ODM) \ --dirmap /product:$(TARGET_OUT_PRODUCT) \ --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \ --dirmap /apex:$(APEX_OUT) \ ifdef PRODUCT_SHIPPING_API_LEVEL check_vintf_compatible_args += --property ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL) endif # PRODUCT_SHIPPING_API_LEVEL $(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_ARGS := $(check_vintf_compatible_args) $(check_vintf_compatible_log): PRIVATE_CHECK_VINTF_DEPS := $(check_vintf_compatible_deps) # Check ODM SKU=(empty) case when: # - ODM_MANIFEST_FILES is not empty; OR # - ODM_MANIFEST_FILES is empty AND ODM_MANIFEST_SKUS is empty (only ODM manifest fragments are used) $(check_vintf_compatible_log): PRIVATE_ODM_SKUS := \ $(if $(ODM_MANIFEST_FILES),EMPTY_ODM_SKU_PLACEHOLDER,\ $(if $(ODM_MANIFEST_SKUS),,EMPTY_ODM_SKU_PLACEHOLDER)) \ $(ODM_MANIFEST_SKUS) # Check vendor SKU=(empty) case when: # - DEVICE_MANIFEST_FILE is not empty; OR # - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used) $(check_vintf_compatible_log): PRIVATE_VENDOR_SKUS := \ $(if $(DEVICE_MANIFEST_FILE),EMPTY_VENDOR_SKU_PLACEHOLDER,\ $(if $(DEVICE_MANIFEST_SKUS),,EMPTY_VENDOR_SKU_PLACEHOLDER)) \ $(DEVICE_MANIFEST_SKUS) $(check_vintf_compatible_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_compatible_deps) @echo "PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS=$(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS)" > $@ @echo -n -e 'Deps: \n ' >> $@ @sed 's/ /\n /g' <<< "$(PRIVATE_CHECK_VINTF_DEPS)" >> $@ @echo -n -e 'Args: \n ' >> $@ @cat <<< "$(PRIVATE_CHECK_VINTF_ARGS)" >> $@ $(foreach odm_sku,$(PRIVATE_ODM_SKUS), $(foreach vendor_sku,$(PRIVATE_VENDOR_SKUS), \ echo "For ODM SKU = $(odm_sku), vendor SKU = $(vendor_sku)" >> $@; \ ( $< --check-compat $(PRIVATE_CHECK_VINTF_ARGS) \ --property ro.boot.product.hardware.sku=$(filter-out EMPTY_ODM_SKU_PLACEHOLDER,$(odm_sku)) \ --property ro.boot.product.vendor.sku=$(filter-out EMPTY_VENDOR_SKU_PLACEHOLDER,$(vendor_sku)) \ >> $@ 2>&1 ) || (cat $@ && exit 1); )) $(call declare-1p-target,$(check_vintf_compatible_log)) check_vintf_compatible_log := check_vintf_compatible_args := check_vintf_compatible_deps := endif # BUILDING_SYSTEM_EXT_IMAGE equals BOARD_USES_SYSTEM_EXTIMAGE endif # BUILDING_PRODUCT_IMAGE equals BOARD_USES_PRODUCTIMAGE endif # BUILDING_ODM_IMAGE equals BOARD_USES_ODMIMAGE endif # check_vintf_has_vendor endif # check_vintf_has_system endif # PRODUCT_ENFORCE_VINTF_MANIFEST # Add all logs of VINTF checks to dist builds droid_targets: $(check_vintf_all_deps) $(call dist-for-goals, droid_targets, $(check_vintf_all_deps)) # Helper alias to check all VINTF of current build. .PHONY: check-vintf-all check-vintf-all: $(check_vintf_all_deps) $(foreach file,$^,echo "$(file)"; cat "$(file)"; echo;) check_vintf_has_vendor := check_vintf_has_system := check_vintf_common_srcs := check_vintf_all_deps := intermediates := endif # !TARGET_BUILD_UNBUNDLED # ----------------------------------------------------------------- # Check image sizes <= size of super partition ifeq (,$(TARGET_BUILD_UNBUNDLED)) ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) PARTITIONS_AND_OTHER_IN_SUPER := $(BOARD_SUPER_PARTITION_PARTITION_LIST) # Add the system other image to the misc_info. Because factory ota may install system_other to the super partition. ifdef BUILDING_SYSTEM_OTHER_IMAGE PARTITIONS_AND_OTHER_IN_SUPER += system_other endif # BUILDING_SYSTEM_OTHER_IMAGE # $(1): misc_info.txt # #(2): optional log file define check-all-partition-sizes-target mkdir -p $(dir $(1)) rm -f $(1) $(call dump-super-image-info, $(1)) $(foreach partition,$(PARTITIONS_AND_OTHER_IN_SUPER), \ echo "$(partition)_image="$(call images-for-partitions,$(partition)) >> $(1);) $(CHECK_PARTITION_SIZES) $(if $(2),--logfile $(2),-v) $(1) endef check_all_partition_sizes_log := $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/check_all_partition_sizes.log droid_targets: $(check_all_partition_sizes_log) $(call dist-for-goals, droid_targets, $(check_all_partition_sizes_log)) $(check_all_partition_sizes_log): \ $(CHECK_PARTITION_SIZES) \ $(call images-for-partitions,$(PARTITIONS_AND_OTHER_IN_SUPER)) $(call check-all-partition-sizes-target, \ $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/misc_info.txt, \ $@) $(call declare-1p-target,$(check_all_partition_sizes_log)) .PHONY: check-all-partition-sizes check-all-partition-sizes: $(check_all_partition_sizes_log) .PHONY: check-all-partition-sizes-nodeps check-all-partition-sizes-nodeps: $(call check-all-partition-sizes-target, \ $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes-nodeps)/misc_info.txt) endif # PRODUCT_BUILD_SUPER_PARTITION endif # !TARGET_BUILD_UNBUNDLED # ----------------------------------------------------------------- # bring in the installer image generation defines if necessary ifeq ($(TARGET_USE_DISKINSTALLER),true) include bootable/diskinstaller/config.mk endif # ----------------------------------------------------------------- # host tools needed to build dist and OTA packages ifeq ($(BUILD_OS),darwin) build_ota_package := false build_otatools_package := false else # Set build_ota_package, and allow opt-out below. build_ota_package := true ifeq ($(TARGET_SKIP_OTA_PACKAGE),true) build_ota_package := false endif ifneq (,$(filter address, $(SANITIZE_TARGET))) build_ota_package := false endif ifeq ($(TARGET_PRODUCT),sdk) build_ota_package := false endif # A target without a kernel may be one of the following: # - A generic target. In this case, the OTA package usually isn't built. # PRODUCT_BUILD_GENERIC_OTA_PACKAGE may be set to true to force OTA package # generation. # - A real device target, with TARGET_NO_KERNEL set to true and # BOARD_PREBUILT_BOOTIMAGE set. In this case, it is valid to generate # an OTA package. ifneq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) ifneq ($(filter generic%,$(TARGET_DEVICE)),) build_ota_package := false endif ifeq ($(INSTALLED_BOOTIMAGE_TARGET),) ifeq ($(TARGET_NO_KERNEL),true) build_ota_package := false endif endif # INSTALLED_BOOTIMAGE_TARGET == "" ifeq ($(recovery_fstab),) ifeq ($(filter $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab,$(INTERNAL_RECOVERYIMAGE_FILES)),) build_ota_package := false endif endif endif # PRODUCT_BUILD_GENERIC_OTA_PACKAGE # Set build_otatools_package, and allow opt-out below. build_otatools_package := true ifeq ($(TARGET_SKIP_OTATOOLS_PACKAGE),true) build_otatools_package := false endif endif ifeq ($(build_otatools_package),true) INTERNAL_OTATOOLS_MODULES := \ aapt2 \ add_img_to_target_files \ apksigner \ append2simg \ avbtool \ blk_alloc_to_base_fs \ boot_signer \ brillo_update_payload \ brotli \ bsdiff \ build_image \ build_mixed_kernels_ramdisk_host \ build_super_image \ build_verity_metadata \ build_verity_tree \ care_map_generator \ check_ota_package_signature \ check_target_files_signatures \ check_target_files_vintf \ checkvintf \ create_brick_ota \ delta_generator \ e2fsck \ e2fsdroid \ fc_sort \ fec \ fsck.erofs \ fsck.f2fs \ fs_config \ generate_verity_key \ host_init_verifier \ img2simg \ img_from_target_files \ imgdiff \ initrd_bootconfig \ libconscrypt_openjdk_jni \ lpmake \ lpunpack \ lz4 \ make_f2fs \ make_f2fs_casefold \ merge_ota \ merge_target_files \ mk_combined_img \ mkbootfs \ mkbootimg \ mke2fs \ mke2fs.conf \ mkfs.erofs \ mkf2fsuserimg \ mksquashfs \ mksquashfsimage \ mkuserimg_mke2fs \ ota_extractor \ ota_from_target_files \ repack_bootimg \ secilc \ sefcontext_compile \ sgdisk \ shflags \ sign_apex \ sign_target_files_apks \ sign_virt_apex \ signapk \ simg2img \ sload_f2fs \ toybox \ tune2fs \ unpack_bootimg \ update_device \ update_host_simulator \ validate_target_files \ verity_signer \ verity_verifier \ zipalign \ zucchini \ zip2zip \ # Additional tools to unpack and repack the apex file. INTERNAL_OTATOOLS_MODULES += \ apexd_host \ apexer \ apex_compression_tool \ deapexer \ debugfs_static \ fsck.erofs \ make_erofs \ merge_zips \ resize2fs \ soong_zip \ INTERNAL_OTATOOLS_FILES := \ $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES))) .PHONY: otatools otatools: $(INTERNAL_OTATOOLS_FILES) endif # build_otatools_package # ----------------------------------------------------------------- # fastboot-info.txt FASTBOOT_INFO_VERSION = 1 INSTALLED_FASTBOOT_INFO_TARGET := $(PRODUCT_OUT)/fastboot-info.txt ifdef TARGET_BOARD_FASTBOOT_INFO_FILE $(INSTALLED_FASTBOOT_INFO_TARGET): $(TARGET_BOARD_FASTBOOT_INFO_FILE) rm -f $@ $(call pretty,"Target fastboot-info.txt: $@") $(hide) cp $< $@ else $(INSTALLED_FASTBOOT_INFO_TARGET): rm -f $@ $(call pretty,"Target fastboot-info.txt: $@") $(hide) echo "# fastboot-info for $(TARGET_PRODUCT)" >> $@ $(hide) echo "version $(FASTBOOT_INFO_VERSION)" >> $@ ifneq ($(INSTALLED_BOOTIMAGE_TARGET),) $(hide) echo "flash boot" >> $@ endif ifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),) $(hide) echo "flash init_boot" >> $@ endif ifdef BOARD_PREBUILT_DTBOIMAGE $(hide) echo "flash dtbo" >> $@ endif ifneq ($(INSTALLED_DTIMAGE_TARGET),) $(hide) echo "flash dts dt.img" >> $@ endif ifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),) $(hide) echo "flash vendor_kernel_boot" >> $@ endif ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) echo "flash recovery" >> $@ endif ifeq ($(BOARD_USES_PVMFWIMAGE),true) $(hide) echo "flash pvmfw" >> $@ endif ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) $(hide) echo "flash vendor_boot" >> $@ endif ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(BUILDING_VBMETA_IMAGE),true) $(hide) echo "flash --apply-vbmeta vbmeta" >> $@ endif ifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM))) $(hide) echo "flash vbmeta_system" >> $@ endif ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR))) $(hide) echo "flash vbmeta_vendor" >> $@ endif ifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS))) $(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \ echo "flash vbmeta_$(partition)" >> $@;) endif endif # BOARD_AVB_ENABLE ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST))) $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ $(if $(BOARD_$(call to-upper,$(partition))_IMAGE_NO_FLASHALL),, \ echo "flash $(partition)" >> $@; \ ) \ ) endif $(hide) echo "reboot fastboot" >> $@ $(hide) echo "update-super" >> $@ $(hide) $(foreach partition,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \ echo "flash $(partition)" >> $@;) ifdef BUILDING_SYSTEM_OTHER_IMAGE $(hide) echo "flash --slot-other system system_other.img" >> $@ endif ifdef BUILDING_CACHE_IMAGE $(hide) echo "if-wipe erase cache" >> $@ endif $(hide) echo "if-wipe erase userdata" >> $@ ifeq ($(BOARD_USES_METADATA_PARTITION),true) $(hide) echo "if-wipe erase metadata" >> $@ endif endif # ----------------------------------------------------------------- # misc_info.txt INSTALLED_MISC_INFO_TARGET := $(PRODUCT_OUT)/misc_info.txt ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),) # default to common dir for device vendor tool_extensions := $(TARGET_DEVICE_DIR)/../common else tool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS) endif .KATI_READONLY := tool_extensions # $1: boot image file name define misc_boot_size $(subst .img,_size,$(1))=$(BOARD_KERNEL$(call to-upper,$(subst boot,,$(subst .img,,$(1))))_BOOTIMAGE_PARTITION_SIZE) endef $(INSTALLED_MISC_INFO_TARGET): rm -f $@ $(call pretty,"Target misc_info.txt: $@") $(hide) echo "recovery_api_version=$(RECOVERY_API_VERSION)" >> $@ $(hide) echo "fstab_version=$(RECOVERY_FSTAB_VERSION)" >> $@ ifdef BOARD_FLASH_BLOCK_SIZE $(hide) echo "blocksize=$(BOARD_FLASH_BLOCK_SIZE)" >> $@ endif ifneq ($(strip $(BOARD_BOOTIMAGE_PARTITION_SIZE))$(strip $(BOARD_KERNEL_BINARIES)),) $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),\ echo "$(call misc_boot_size,$(notdir $(b)))" >> $@;) endif ifeq ($(INSTALLED_BOOTIMAGE_TARGET),) $(hide) echo "no_boot=true" >> $@ else echo "boot_images=$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(notdir $(b)))" >> $@ endif ifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),) $(hide) echo "init_boot=true" >> $@ $(hide) echo "init_boot_size=$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)" >> $@ endif ifeq ($(BOARD_RAMDISK_USE_LZ4),true) echo "lz4_ramdisks=true" >> $@ endif ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) echo "vendor_boot=true" >> $@ echo "vendor_boot_size=$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)" >> $@ endif ifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),) echo "vendor_kernel_boot=true" >> $@ echo "vendor_kernel_boot_size=$(BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE)" >> $@ endif ifeq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) echo "no_recovery=true" >> $@ endif ifdef BOARD_INCLUDE_RECOVERY_DTBO $(hide) echo "include_recovery_dtbo=true" >> $@ endif ifdef BOARD_INCLUDE_RECOVERY_ACPIO $(hide) echo "include_recovery_acpio=true" >> $@ endif ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE $(hide) echo "recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >> $@ endif ifdef TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS @# TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS can be empty to indicate that nothing but defaults should be used. $(hide) echo "recovery_mount_options=$(TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)" >> $@ else $(hide) echo "recovery_mount_options=$(DEFAULT_TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)" >> $@ endif $(hide) echo "tool_extensions=$(tool_extensions)" >> $@ $(hide) echo "default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)" >> $@ ifdef PRODUCT_EXTRA_OTA_KEYS $(hide) echo "extra_ota_keys=$(PRODUCT_EXTRA_OTA_KEYS)" >> $@ endif ifdef PRODUCT_EXTRA_RECOVERY_KEYS $(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $@ endif $(hide) echo 'mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)' >> $@ $(hide) echo 'recovery_mkbootimg_args=$(BOARD_RECOVERY_MKBOOTIMG_ARGS)' >> $@ $(hide) echo 'mkbootimg_version_args=$(INTERNAL_MKBOOTIMG_VERSION_ARGS)' >> $@ $(hide) echo 'mkbootimg_init_args=$(BOARD_MKBOOTIMG_INIT_ARGS)' >> $@ $(hide) echo "multistage_support=1" >> $@ $(hide) echo "blockimgdiff_versions=3,4" >> $@ ifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) $(hide) echo "build_generic_ota_package=true" >> $@ endif ifneq ($(OEM_THUMBPRINT_PROPERTIES),) # OTA scripts are only interested in fingerprint related properties $(hide) echo "oem_fingerprint_properties=$(OEM_THUMBPRINT_PROPERTIES)" >> $@ endif ifneq (,$(filter address, $(SANITIZE_TARGET))) # We need to create userdata.img with real data because the instrumented libraries are in userdata.img. $(hide) echo "userdata_img_with_data=true" >> $@ endif ifeq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) $(hide) echo "full_recovery_image=true" >> $@ endif ifdef BOARD_USES_VENDORIMAGE $(hide) echo "board_uses_vendorimage=true" >> $@ endif ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(BUILDING_VBMETA_IMAGE),true) $(hide) echo "avb_building_vbmeta_image=true" >> $@ endif # BUILDING_VBMETA_IMAGE $(hide) echo "avb_enable=true" >> $@ $(hide) echo "avb_vbmeta_key_path=$(BOARD_AVB_KEY_PATH)" >> $@ $(hide) echo "avb_vbmeta_algorithm=$(BOARD_AVB_ALGORITHM)" >> $@ $(hide) echo "avb_vbmeta_args=$(BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS)" >> $@ $(hide) echo "avb_boot_add_hash_footer_args=$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_BOOT_KEY_PATH $(hide) echo "avb_boot_key_path=$(BOARD_AVB_BOOT_KEY_PATH)" >> $@ $(hide) echo "avb_boot_algorithm=$(BOARD_AVB_BOOT_ALGORITHM)" >> $@ $(hide) echo "avb_boot_rollback_index_location=$(BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_BOOT_KEY_PATH $(hide) echo "avb_init_boot_add_hash_footer_args=$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_INIT_BOOT_KEY_PATH $(hide) echo "avb_init_boot_key_path=$(BOARD_AVB_INIT_BOOT_KEY_PATH)" >> $@ $(hide) echo "avb_init_boot_algorithm=$(BOARD_AVB_INIT_BOOT_ALGORITHM)" >> $@ $(hide) echo "avb_init_boot_rollback_index_location=$(BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_INIT_BOOT_KEY_PATH echo "avb_vendor_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_VENDOR_BOOT_KEY_PATH echo "avb_vendor_boot_key_path=$(BOARD_AVB_VENDOR_BOOT_KEY_PATH)" >> $@ echo "avb_vendor_boot_algorithm=$(BOARD_AVB_VENDOR_BOOT_ALGORITHM)" >> $@ echo "avb_vendor_boot_rollback_index_location=$(BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_VENDOR_BOOT_KEY_PATH echo "avb_vendor_kernel_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH echo "avb_vendor_kernel_boot_key_path=$(BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH)" >> $@ echo "avb_vendor_kernel_boot_algorithm=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM)" >> $@ echo "avb_vendor_kernel_boot_rollback_index_location=$(BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH $(hide) echo "avb_recovery_add_hash_footer_args=$(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_RECOVERY_KEY_PATH $(hide) echo "avb_recovery_key_path=$(BOARD_AVB_RECOVERY_KEY_PATH)" >> $@ $(hide) echo "avb_recovery_algorithm=$(BOARD_AVB_RECOVERY_ALGORITHM)" >> $@ $(hide) echo "avb_recovery_rollback_index_location=$(BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_RECOVERY_KEY_PATH ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST))) $(hide) echo "custom_images_partition_list=$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST))" >> $@ $(hide) $(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \ echo "$(partition)_image_list=$(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;) endif # BOARD_CUSTOMIMAGES_PARTITION_LIST ifneq (,$(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST))) $(hide) echo "avb_custom_images_partition_list=$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)" >> $@ $(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \ echo "avb_$(partition)_key_path=$(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH)" >> $@; \ echo "avb_$(partition)_algorithm=$(BOARD_AVB_$(call to-upper,$(partition))_ALGORITHM)" >> $@; \ echo "avb_$(partition)_add_hashtree_footer_args=$(BOARD_AVB_$(call to-upper,$(partition))_ADD_HASHTREE_FOOTER_ARGS)" >> $@; \ echo "avb_$(partition)_rollback_index_location=$(BOARD_AVB_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)" >> $@; \ echo "avb_$(partition)_partition_size=$(BOARD_AVB_$(call to-upper,$(partition))_PARTITION_SIZE)" >> $@; \ echo "avb_$(partition)_image_list=$(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;) endif # BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST ifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM))) $(hide) echo "avb_vbmeta_system=$(BOARD_AVB_VBMETA_SYSTEM)" >> $@ $(hide) echo "avb_vbmeta_system_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@ $(hide) echo "avb_vbmeta_system_key_path=$(BOARD_AVB_VBMETA_SYSTEM_KEY_PATH)" >> $@ $(hide) echo "avb_vbmeta_system_algorithm=$(BOARD_AVB_VBMETA_SYSTEM_ALGORITHM)" >> $@ $(hide) echo "avb_vbmeta_system_rollback_index_location=$(BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_VBMETA_SYSTEM ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR))) $(hide) echo "avb_vbmeta_vendor=$(BOARD_AVB_VBMETA_VENDOR)" >> $@ $(hide) echo "avb_vbmeta_vendor_args=$(BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS)" >> $@ $(hide) echo "avb_vbmeta_vendor_key_path=$(BOARD_AVB_VBMETA_VENDOR_KEY_PATH)" >> $@ $(hide) echo "avb_vbmeta_vendor_algorithm=$(BOARD_AVB_VBMETA_VENDOR_ALGORITHM)" >> $@ $(hide) echo "avb_vbmeta_vendor_rollback_index_location=$(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_VBMETA_VENDOR_KEY_PATH ifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS))) $(hide) echo "avb_custom_vbmeta_images_partition_list=$(sort $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS))" >> $@ $(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\ echo "avb_vbmeta_$(partition)=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))" >> $@ ;\ echo "avb_vbmeta_$(partition)_args=$(BOARD_AVB_MAKE_VBMETA_$(call to-upper,$(partition))_IMAGE_ARGS)" >> $@ ;\ echo "avb_vbmeta_$(partition)_key_path=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)" >> $@ ;\ echo "avb_vbmeta_$(partition)_algorithm=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)" >> $@ ;\ echo "avb_vbmeta_$(partition)_rollback_index_location=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)" >> $@ ;) endif # BOARD_AVB_VBMETA_CUSTOM_PARTITIONS endif # BOARD_AVB_ENABLE $(call generate-userimage-prop-dictionary, $@) ifeq ($(AB_OTA_UPDATER),true) @# Include the build type in META/misc_info.txt so the server can easily differentiate production builds. $(hide) echo "build_type=$(TARGET_BUILD_VARIANT)" >> $@ $(hide) echo "ab_update=true" >> $@ endif ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) $(hide) echo "allow_non_ab=true" >> $@ endif ifeq ($(BOARD_NON_AB_OTA_DISABLE_COMPRESSION),true) $(hide) echo "board_non_ab_ota_disable_compression=true" >> $@ endif ifdef BOARD_PREBUILT_DTBOIMAGE $(hide) echo "has_dtbo=true" >> $@ ifeq ($(BOARD_AVB_ENABLE),true) $(hide) echo "dtbo_size=$(BOARD_DTBOIMG_PARTITION_SIZE)" >> $@ $(hide) echo "avb_dtbo_add_hash_footer_args=$(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_DTBO_KEY_PATH $(hide) echo "avb_dtbo_key_path=$(BOARD_AVB_DTBO_KEY_PATH)" >> $@ $(hide) echo "avb_dtbo_algorithm=$(BOARD_AVB_DTBO_ALGORITHM)" >> $@ $(hide) echo "avb_dtbo_rollback_index_location=$(BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_DTBO_KEY_PATH endif # BOARD_AVB_ENABLE endif # BOARD_PREBUILT_DTBOIMAGE ifeq ($(BOARD_USES_PVMFWIMAGE),true) $(hide) echo "has_pvmfw=true" >> $@ ifeq ($(BOARD_AVB_ENABLE),true) $(hide) echo "pvmfw_size=$(BOARD_PVMFWIMAGE_PARTITION_SIZE)" >> $@ $(hide) echo "avb_pvmfw_add_hash_footer_args=$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)" >> $@ ifdef BOARD_AVB_PVMFW_KEY_PATH $(hide) echo "avb_pvmfw_key_path=$(BOARD_AVB_PVMFW_KEY_PATH)" >> $@ $(hide) echo "avb_pvmfw_algorithm=$(BOARD_AVB_PVMFW_ALGORITHM)" >> $@ $(hide) echo "avb_pvmfw_rollback_index_location=$(BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION)" >> $@ endif # BOARD_AVB_PVMFW_KEY_PATH endif # BOARD_AVB_ENABLE endif # BOARD_USES_PVMFWIMAGE $(call dump-dynamic-partitions-info,$@) @# VINTF checks ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true) $(hide) echo "vintf_enforce=true" >> $@ endif ifdef ODM_MANIFEST_SKUS $(hide) echo "vintf_odm_manifest_skus=$(ODM_MANIFEST_SKUS)" >> $@ endif ifdef ODM_MANIFEST_FILES $(hide) echo "vintf_include_empty_odm_sku=true" >> $@ endif ifdef DEVICE_MANIFEST_SKUS $(hide) echo "vintf_vendor_manifest_skus=$(DEVICE_MANIFEST_SKUS)" >> $@ endif ifdef DEVICE_MANIFEST_FILE $(hide) echo "vintf_include_empty_vendor_sku=true" >> $@ endif ifeq ($(BOARD_BOOTLOADER_IN_UPDATE_PACKAGE),true) $(hide) echo "bootloader_in_update_package=true" >> $@ endif ifeq ($(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE),true) $(hide) echo "exclude_kernel_from_recovery_image=true" >> $@ endif ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),) $(hide) echo "partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)" >> $@ endif $(hide) sort -o $@ $@ $(call declare-0p-target,$(INSTALLED_FASTBOOT_INFO_TARGET)) .PHONY: fastboot_info fastboot_info: $(INSTALLED_FASTBOOT_INFO_TARGET) droidcore-unbundled: $(INSTALLED_FASTBOOT_INFO_TARGET) $(call declare-0p-target,$(INSTALLED_MISC_INFO_TARGET)) .PHONY: misc_info misc_info: $(INSTALLED_MISC_INFO_TARGET) droidcore-unbundled: $(INSTALLED_MISC_INFO_TARGET) # ----------------------------------------------------------------- # A zip of the directories that map to the target filesystem. # This zip can be used to create an OTA package or filesystem image # as a post-build step. # name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif name := $(name)-target_files intermediates := $(call intermediates-dir-for,PACKAGING,target_files) BUILT_TARGET_FILES_DIR := $(intermediates)/$(name).zip.list BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip $(BUILT_TARGET_FILES_PACKAGE): zip_root := $(intermediates)/$(name) $(BUILT_TARGET_FILES_DIR): zip_root := $(intermediates)/$(name) $(BUILT_TARGET_FILES_DIR): intermediates := $(intermediates) ifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),) $(BUILT_TARGET_FILES_DIR): $(SOONG_DEFINED_SYSTEM_IMAGE_PATH) endif # $(1): Directory to copy # $(2): Location to copy it to # The "ls -A" is to skip if $(1) is empty. define package_files-copy-root if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \ mkdir -p $(2) && \ $(ACP) -rd $(strip $(1))/. $(strip $(2))/; \ fi endef built_ota_tools := # We can't build static executables when SANITIZE_TARGET=address ifeq (,$(filter address, $(SANITIZE_TARGET))) ifeq (false,$(AB_OTA_UPDATER)) built_ota_tools += \ $(call intermediates-dir-for,EXECUTABLES,updater)/updater endif endif $(BUILT_TARGET_FILES_DIR): PRIVATE_OTA_TOOLS := $(built_ota_tools) tool_extension := $(wildcard $(tool_extensions)/releasetools.py) $(BUILT_TARGET_FILES_DIR): PRIVATE_TOOL_EXTENSION := $(tool_extension) updater_dep := ifeq ($(AB_OTA_UPDATER),true) updater_dep += system/update_engine/update_engine.conf $(call declare-1p-target,system/update_engine/update_engine.conf,system/update_engine) updater_dep += external/zucchini/version_info.h $(call declare-license-metadata,external/zucchini/version_info.h,legacy_notice,notice,external/zucchini/LICENSE,external/zucchini) updater_dep += $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so endif # Build OTA tools if non-A/B is allowed ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) updater_dep += $(built_ota_tools) endif $(BUILT_TARGET_FILES_DIR): $(updater_dep) # If we are using recovery as boot, output recovery files to BOOT/. # If we are moving recovery resources to vendor_boot, output recovery files to VENDOR_BOOT/. ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := BOOT else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) $(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := VENDOR_BOOT else $(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := RECOVERY endif ifeq ($(AB_OTA_UPDATER),true) ifdef OSRELEASED_DIRECTORY $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version endif endif ifneq ($(AB_OTA_PARTITIONS),) ifneq ($(AB_OTA_UPDATER),true) $(error AB_OTA_UPDATER must be true when defining AB_OTA_PARTITIONS) endif endif # Run fs_config while creating the target files package # $1: root directory # $2: add prefix define fs_config (cd $(1); find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -R "$(2)" endef define filter-out-missing-vendor $(if $(INSTALLED_VENDORIMAGE_TARGET),$(1),$(filter-out vendor,$(1))) endef define filter-out-missing-vendor_dlkm $(if $(INSTALLED_VENDOR_DLKMIMAGE_TARGET),$(1),$(filter-out vendor_dlkm,$(1))) endef define filter-out-missing-odm $(if $(INSTALLED_ODMIMAGE_TARGET),$(1),$(filter-out odm,$(1))) endef define filter-out-missing-odm_dlkm $(if $(INSTALLED_ODM_DLKMIMAGE_TARGET),$(1),$(filter-out odm_dlkm,$(1))) endef define filter-out-missing-system_dlkm $(if $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET),$(1),$(filter-out system_dlkm,$(1))) endef # Filter out vendor,vendor_dlkm,odm,odm_dlkm,system_dlkm from the list for AOSP targets. # $(1): list define filter-out-missing-partitions $(call filter-out-missing-vendor,\ $(call filter-out-missing-vendor_dlkm,\ $(call filter-out-missing-odm,\ $(call filter-out-missing-odm_dlkm,\ $(call filter-out-missing-system_dlkm,$(1)))))) endef # Information related to dynamic partitions and virtual A/B. This information # is needed for building the super image (see dump-super-image-info) and # building OTA packages. # $(1): file define dump-dynamic-partitions-info $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)), \ echo "use_dynamic_partitions=true" >> $(1)) $(if $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)), \ echo "dynamic_partition_retrofit=true" >> $(1)) echo "lpmake=$(notdir $(LPMAKE))" >> $(1) $(if $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)), $(if $(BOARD_SUPER_PARTITION_SIZE), \ echo "build_super_partition=true" >> $(1))) $(if $(BUILDING_SUPER_EMPTY_IMAGE), \ echo "build_super_empty_partition=true" >> $(1)) $(if $(filter true,$(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE)), \ echo "build_retrofit_dynamic_partitions_ota_package=true" >> $(1)) echo "super_metadata_device=$(BOARD_SUPER_PARTITION_METADATA_DEVICE)" >> $(1) $(if $(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \ echo "super_block_devices=$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)" >> $(1)) $(foreach device,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \ echo "super_$(device)_device_size=$(BOARD_SUPER_PARTITION_$(call to-upper,$(device))_DEVICE_SIZE)" >> $(1);) $(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \ echo "dynamic_partition_list=$(sort $(call filter-out-missing-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))" >> $(1)) $(if $(BOARD_SUPER_PARTITION_GROUPS), echo "super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)" >> $(1)) $(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \ echo "super_$(group)_group_size=$(BOARD_$(call to-upper,$(group))_SIZE)" >> $(1); \ $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \ echo "super_$(group)_partition_list=$(strip $(call filter-out-missing-partitions,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST)))" >> $(1);)) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)), \ echo "build_non_sparse_super_partition=true" >> $(1)) $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)), \ echo "build_non_sparse_super_partition=true" >> $(1)) $(if $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)), \ echo "super_image_in_update_package=true" >> $(1)) $(if $(BOARD_SUPER_PARTITION_SIZE), \ echo "super_partition_size=$(BOARD_SUPER_PARTITION_SIZE)" >> $(1)) $(if $(BOARD_SUPER_PARTITION_ALIGNMENT), \ echo "super_partition_alignment=$(BOARD_SUPER_PARTITION_ALIGNMENT)" >> $(1)) $(if $(BOARD_SUPER_PARTITION_WARN_LIMIT), \ echo "super_partition_warn_limit=$(BOARD_SUPER_PARTITION_WARN_LIMIT)" >> $(1)) $(if $(BOARD_SUPER_PARTITION_ERROR_LIMIT), \ echo "super_partition_error_limit=$(BOARD_SUPER_PARTITION_ERROR_LIMIT)" >> $(1)) $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)), \ echo "virtual_ab=true" >> $(1)) $(if $(filter true,$(PRODUCT_VIRTUAL_AB_COMPRESSION)), \ echo "virtual_ab_compression=true" >> $(1)) # This value controls the compression algorithm used for VABC # valid options are defined in system/core/fs_mgr/libsnapshot/cow_writer.cpp # e.g. "none", "gz", "brotli" $(if $(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD), \ echo "virtual_ab_compression_method=$(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD)" >> $(1)) $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)), \ echo "virtual_ab_retrofit=true" >> $(1)) $(if $(PRODUCT_VIRTUAL_AB_COW_VERSION), \ echo "virtual_ab_cow_version=$(PRODUCT_VIRTUAL_AB_COW_VERSION)" >> $(1)) $(if $(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR), \ echo "virtual_ab_compression_factor=$(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR)" >> $(1)) endef # Copy an image file to a directory and generate a block list map file from the image, # only if the map_file_generator supports the file system. # Otherwise, skip generating map files as well as copying images. The image will be # generated from the $(ADD_IMG_TO_TARGET_FILES) to generate the map file with it. # $(1): path of the image file # $(2): target out directory # $(3): image name to generate a map file. skip generating map file if empty define copy-image-and-generate-map $(if $(COPY_IMAGES_FOR_TARGET_FILES_ZIP), \ $(eval _supported_fs_for_map_file_generator := erofs ext%) \ $(eval _img := $(call to-upper,$(3))) \ $(if $(3),$(eval _map_fs_type := $(BOARD_$(_img)IMAGE_FILE_SYSTEM_TYPE)),\ $(eval _no_map_file := "true")) \ $(if $(filter $(_supported_fs_for_map_file_generator),$(_map_fs_type))$(_no_map_file),\ mkdir -p $(2); \ cp $(1) $(2); \ $(if $(3),$(HOST_OUT_EXECUTABLES)/map_file_generator $(1) $(2)/$(3).map)) \ $(eval _img :=) \ $(eval _map_fs_type :=) \ $(eval _no_map_file :=) \ ) endef # By conditionally including the dependency of the target files package on the # full system image deps, we speed up builds that do not build the system # image. ifdef BUILDING_SYSTEM_IMAGE $(BUILT_TARGET_FILES_DIR): $(FULL_SYSTEMIMAGE_DEPS) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMIMAGE) endif else # releasetools may need the system build.prop even when building a # system-image-less product. $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BUILD_PROP_TARGET) endif ifdef BUILDING_USERDATA_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_USERDATAIMAGE_FILES) endif ifdef BUILDING_SYSTEM_OTHER_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEMOTHERIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMOTHERIMAGE_TARGET) endif endif ifdef BUILDING_VENDOR_BOOT_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_RAMDISK_FILES) $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS) $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) # The vendor ramdisk may be built from the recovery ramdisk. ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP) endif endif ifdef BUILDING_VENDOR_KERNEL_BOOT_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) endif ifdef BUILDING_RECOVERY_IMAGE # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other # BUILT_TARGET_FILES_PACKAGE dependencies because currently there're cp/rsync/rm # commands in build-recoveryimage-target, which would touch the files under # TARGET_RECOVERY_OUT and race with packaging target-files.zip. ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET) else $(BUILT_TARGET_FILES_DIR): $(INSTALLED_RECOVERYIMAGE_TARGET) endif $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERYIMAGE_FILES) endif # Conditionally depend on the image files if the image is being built so the # target-files.zip rule doesn't wait on the image creation rule, or the image # if it is coming from a prebuilt. ifdef BUILDING_VENDOR_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDORIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDORIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_VENDORIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDORIMAGE_TARGET) endif ifdef BUILDING_PRODUCT_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_PRODUCTIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_PRODUCTIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_PRODUCTIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_PRODUCTIMAGE_TARGET) endif ifdef BUILDING_SYSTEM_EXT_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_EXTIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_EXTIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) endif ifneq (,$(BUILDING_BOOT_IMAGE)$(BUILDING_INIT_BOOT_IMAGE)) $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RAMDISK_FILES) endif # BUILDING_BOOT_IMAGE != "" || BUILDING_INIT_BOOT_IMAGE != "" ifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES))) $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET) endif ifdef BUILDING_ODM_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODMIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_ODMIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_ODMIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODMIMAGE_TARGET) endif ifdef BUILDING_VENDOR_DLKM_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_DLKMIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDOR_DLKMIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) endif ifdef BUILDING_ODM_DLKM_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODM_DLKMIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_ODM_DLKMIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODM_DLKMIMAGE_TARGET) endif ifdef BUILDING_SYSTEM_DLKM_IMAGE $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES) ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_DLKMIMAGE_TARGET) endif else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) endif ifeq ($(BUILD_QEMU_IMAGES),true) MK_VBMETA_BOOT_KERNEL_CMDLINE_SH := device/generic/goldfish/tools/mk_vbmeta_boot_params.sh $(BUILT_TARGET_FILES_DIR): $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) endif ifdef BOARD_PREBUILT_BOOTLOADER $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTLOADER_MODULE) droidcore-unbundled: $(INSTALLED_BOOTLOADER_MODULE) endif # Depending on the various images guarantees that the underlying # directories are up-to-date. $(BUILT_TARGET_FILES_DIR): \ $(INSTALLED_RADIOIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_DTBOIMAGE_TARGET) \ $(INSTALLED_PVMFWIMAGE_TARGET) \ $(INSTALLED_PVMFW_BINARY_TARGET) \ $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \ $(INSTALLED_CUSTOMIMAGES_TARGET) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ $(INSTALLED_KERNEL_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_DTBIMAGE_TARGET) \ $(INSTALLED_2NDBOOTLOADER_TARGET) \ $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ $(BUILT_RAMDISK_16K_TARGET) \ $(BUILT_KERNEL_16K_TARGET) \ $(BUILT_BOOTIMAGE_16K_TARGET) \ $(INSTALLED_DTBOIMAGE_16KB_TARGET) \ $(BOARD_PREBUILT_DTBOIMAGE) \ $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \ $(BOARD_RECOVERY_ACPIO) \ $(PRODUCT_SYSTEM_BASE_FS_PATH) \ $(PRODUCT_VENDOR_BASE_FS_PATH) \ $(PRODUCT_PRODUCT_BASE_FS_PATH) \ $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ $(PRODUCT_ODM_BASE_FS_PATH) \ $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ $(LPMAKE) \ $(SELINUX_FC) \ $(INSTALLED_MISC_INFO_TARGET) \ $(INSTALLED_FASTBOOT_INFO_TARGET) \ $(APKCERTS_FILE) \ $(APEX_KEYS_FILE) \ $(SOONG_ZIP) \ $(HOST_OUT_EXECUTABLES)/fs_config \ $(HOST_OUT_EXECUTABLES)/map_file_generator \ $(ADD_IMG_TO_TARGET_FILES) \ $(MAKE_RECOVERY_PATCH) \ $(BUILT_KERNEL_CONFIGS_FILE) \ $(BUILT_KERNEL_VERSION_FILE) \ | $(ACP) @echo "Building target files: $@" $(hide) rm -rf $@ $@.list $(zip_root) $(hide) mkdir -p $(dir $@) $(zip_root) ifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))$(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) @# Components of the recovery image $(hide) mkdir -p $(zip_root)/$(PRIVATE_RECOVERY_OUT) # Exclude recovery files in the default vendor ramdisk if including a standalone # recovery ramdisk in vendor_boot. ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) $(hide) $(call package_files-copy-root, \ $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/$(PRIVATE_RECOVERY_OUT)/RAMDISK) endif ifdef INSTALLED_KERNEL_TARGET ifneq (,$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/ else ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) cp $(firstword $(INSTALLED_KERNEL_TARGET)) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/kernel endif endif ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT))) ifdef INSTALLED_2NDBOOTLOADER_TARGET cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/second endif ifdef BOARD_INCLUDE_RECOVERY_DTBO ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE cp $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo else cp $(BOARD_PREBUILT_DTBOIMAGE) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_dtbo endif endif # BOARD_INCLUDE_RECOVERY_DTBO ifdef BOARD_INCLUDE_RECOVERY_ACPIO cp $(BOARD_RECOVERY_ACPIO) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_acpio endif ifdef INSTALLED_DTBIMAGE_TARGET cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/dtb endif ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) ifdef INTERNAL_KERNEL_CMDLINE echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/cmdline endif # INTERNAL_KERNEL_CMDLINE != "" endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true ifdef BOARD_KERNEL_BASE echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/base endif ifdef BOARD_KERNEL_PAGESIZE echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/pagesize endif endif # not (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT) endif # INSTALLED_RECOVERYIMAGE_TARGET defined or BOARD_USES_RECOVERY_AS_BOOT is true @# Components of the boot image $(hide) mkdir -p $(zip_root)/BOOT $(hide) mkdir -p $(zip_root)/ROOT $(hide) $(call package_files-copy-root, \ $(TARGET_ROOT_OUT),$(zip_root)/ROOT) @# If we are using recovery as boot, this is already done when processing recovery. ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(hide) $(call package_files-copy-root, \ $(TARGET_RAMDISK_OUT),$(zip_root)/BOOT/RAMDISK) ifdef INSTALLED_KERNEL_TARGET $(hide) cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/ endif ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) echo "$(GENERIC_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline else ifndef INSTALLED_VENDOR_BOOTIMAGE_TARGET # && BOARD_USES_GENERIC_KERNEL_IMAGE != true echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline ifdef INSTALLED_2NDBOOTLOADER_TARGET cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second endif ifdef INSTALLED_DTBIMAGE_TARGET cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/BOOT/dtb endif ifdef BOARD_KERNEL_BASE echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base endif ifdef BOARD_KERNEL_PAGESIZE echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/BOOT/pagesize endif endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true endif # BOARD_USES_RECOVERY_AS_BOOT not true $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\ mkdir -p $(zip_root)/RADIO; \ cp $(t) $(zip_root)/RADIO/$(notdir $(t));) ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET mkdir -p $(zip_root)/VENDOR_BOOT $(call package_files-copy-root, \ $(TARGET_VENDOR_RAMDISK_OUT),$(zip_root)/VENDOR_BOOT/RAMDISK) ifdef INSTALLED_DTBIMAGE_TARGET ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),true) cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_BOOT/dtb endif endif # end of INSTALLED_DTBIMAGE_TARGET ifdef INTERNAL_VENDOR_BOOTCONFIG_TARGET cp $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) $(zip_root)/VENDOR_BOOT/vendor_bootconfig endif ifdef BOARD_KERNEL_BASE echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/VENDOR_BOOT/base endif ifdef BOARD_KERNEL_PAGESIZE echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/VENDOR_BOOT/pagesize endif echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/VENDOR_BOOT/vendor_cmdline ifdef INTERNAL_VENDOR_RAMDISK_FRAGMENTS echo "$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS)" > "$(zip_root)/VENDOR_BOOT/vendor_ramdisk_fragments" $(foreach vendor_ramdisk_fragment,$(INTERNAL_VENDOR_RAMDISK_FRAGMENTS), \ mkdir -p $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment); \ echo "$(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).MKBOOTIMG_ARGS)" > "$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/mkbootimg_args"; \ $(eval prebuilt_ramdisk := $(BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).PREBUILT)) \ $(if $(prebuilt_ramdisk), \ cp "$(prebuilt_ramdisk)" "$(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/prebuilt_ramdisk";, \ $(call package_files-copy-root, \ $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR), \ $(zip_root)/VENDOR_BOOT/RAMDISK_FRAGMENTS/$(vendor_ramdisk_fragment)/RAMDISK); \ )) endif # INTERNAL_VENDOR_RAMDISK_FRAGMENTS != "" endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET ifdef INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET mkdir -p $(zip_root)/VENDOR_KERNEL_BOOT $(call package_files-copy-root, \ $(TARGET_VENDOR_KERNEL_RAMDISK_OUT),$(zip_root)/VENDOR_KERNEL_BOOT/RAMDISK) ifdef INSTALLED_DTBIMAGE_TARGET cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/VENDOR_KERNEL_BOOT/dtb endif ifdef BOARD_KERNEL_PAGESIZE echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/VENDOR_KERNEL_BOOT/pagesize endif endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET ifdef BUILDING_SYSTEM_IMAGE @# Contents of the system image ifneq ($(SOONG_DEFINED_SYSTEM_IMAGE_PATH),) $(hide) $(call package_files-copy-root, \ $(SOONG_DEFINED_SYSTEM_IMAGE_BASE)/system/system,$(zip_root)/SYSTEM) else $(hide) $(call package_files-copy-root, \ $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM) endif else ifdef INSTALLED_BUILD_PROP_TARGET @# Copy the system build.prop even if not building a system image @# because add_img_to_target_files may need it to build other partition @# images. $(hide) mkdir -p "$(zip_root)/SYSTEM" $(hide) cp "$(INSTALLED_BUILD_PROP_TARGET)" "$(patsubst $(TARGET_OUT)/%,$(zip_root)/SYSTEM/%,$(INSTALLED_BUILD_PROP_TARGET))" endif ifdef BUILDING_USERDATA_IMAGE @# Contents of the data image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_DATA),$(zip_root)/DATA) endif ifdef BUILDING_VENDOR_IMAGE @# Contents of the vendor image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_VENDOR),$(zip_root)/VENDOR) endif ifdef BUILDING_PRODUCT_IMAGE @# Contents of the product image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_PRODUCT),$(zip_root)/PRODUCT) endif ifdef BUILDING_SYSTEM_EXT_IMAGE @# Contents of the system_ext image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_SYSTEM_EXT),$(zip_root)/SYSTEM_EXT) endif ifdef BUILDING_ODM_IMAGE @# Contents of the odm image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_ODM),$(zip_root)/ODM) endif ifdef BUILDING_VENDOR_DLKM_IMAGE @# Contents of the vendor_dlkm image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_VENDOR_DLKM),$(zip_root)/VENDOR_DLKM) endif ifdef BUILDING_ODM_DLKM_IMAGE @# Contents of the odm_dlkm image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_ODM_DLKM),$(zip_root)/ODM_DLKM) endif ifdef BUILDING_SYSTEM_DLKM_IMAGE @# Contents of the system_dlkm image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_SYSTEM_DLKM),$(zip_root)/SYSTEM_DLKM) endif ifdef BUILDING_SYSTEM_OTHER_IMAGE @# Contents of the system_other image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_SYSTEM_OTHER),$(zip_root)/SYSTEM_OTHER) endif @# Extra contents of the OTA package $(hide) mkdir -p $(zip_root)/OTA $(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/ ifdef BUILDING_RAMDISK_IMAGE ifeq (true,$(BOARD_IMG_USE_RAMDISK)) @# Contents of the ramdisk image $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_RAMDISK_TARGET) $(zip_root)/IMAGES/ endif endif ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(built_ota_tools),) $(hide) mkdir -p $(zip_root)/OTA/bin $(hide) cp $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/ endif endif @# Files that do not end up in any images, but are necessary to @# build them. $(hide) mkdir -p $(zip_root)/META $(hide) cp $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt $(hide) cp $(APEX_KEYS_FILE) $(zip_root)/META/apexkeys.txt ifneq ($(tool_extension),) $(hide) cp $(PRIVATE_TOOL_EXTENSION) $(zip_root)/META/ endif $(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt $(hide) cp $(SELINUX_FC) $(zip_root)/META/file_contexts.bin $(hide) cp $(INSTALLED_MISC_INFO_TARGET) $(zip_root)/META/misc_info.txt ifneq ($(INSTALLED_FASTBOOT_INFO_TARGET),) $(hide) cp $(INSTALLED_FASTBOOT_INFO_TARGET) $(zip_root)/META/fastboot-info.txt endif ifneq ($(PRODUCT_SYSTEM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_SYSTEM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_BASE_FS_PATH)) endif ifneq ($(PRODUCT_VENDOR_BASE_FS_PATH),) $(hide) cp $(PRODUCT_VENDOR_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_BASE_FS_PATH)) endif ifneq ($(PRODUCT_PRODUCT_BASE_FS_PATH),) $(hide) cp $(PRODUCT_PRODUCT_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_PRODUCT_BASE_FS_PATH)) endif ifneq ($(PRODUCT_SYSTEM_EXT_BASE_FS_PATH),) $(hide) cp $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH)) endif ifneq ($(PRODUCT_ODM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_ODM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_ODM_BASE_FS_PATH)) endif ifneq ($(PRODUCT_VENDOR_DLKM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH)) endif ifneq ($(PRODUCT_ODM_DLKM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_ODM_DLKM_BASE_FS_PATH)) endif ifneq ($(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH),) $(hide) cp $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH)) endif ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \ $(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root) endif endif ifeq ($(AB_OTA_UPDATER),true) @# When using the A/B updater, include the updater config files in the zip. $(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt $(hide) cp $(TOPDIR)external/zucchini/version_info.h $(zip_root)/META/zucchini_config.txt $(hide) cp $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so $(zip_root)/META/liblz4.so $(hide) for part in $(sort $(AB_OTA_PARTITIONS)); do \ echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \ done $(hide) for conf in $(strip $(AB_OTA_POSTINSTALL_CONFIG)); do \ echo "$${conf}" >> $(zip_root)/META/postinstall_config.txt; \ done ifdef OSRELEASED_DIRECTORY $(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt $(hide) cp $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt $(hide) cp $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version $(zip_root)/META/system_version.txt endif endif ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) @# If breakpad symbols have been generated, add them to the zip. $(hide) cp -R $(TARGET_OUT_BREAKPAD) $(zip_root)/BREAKPAD endif ifdef BOARD_PREBUILT_VENDOR_BOOTIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_VENDORIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_VENDORIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_PRODUCTIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_PRODUCTIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(INSTALLED_INIT_BOOT_IMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ endif ifndef BOARD_PREBUILT_BOOTIMAGE ifneq (,$(strip $(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))) ifdef INSTALLED_BOOTIMAGE_TARGET $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/ endif # INSTALLED_BOOTIMAGE_TARGET endif # INTERNAL_PREBUILT_BOOTIMAGE != "" || BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES == true else # BOARD_PREBUILT_BOOTIMAGE is defined $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ endif # BOARD_PREBUILT_BOOTIMAGE ifdef BOARD_PREBUILT_ODMIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_ODMIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_ODM_DLKMIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(zip_root)/IMAGES/ endif ifdef BOARD_PREBUILT_DTBOIMAGE $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(INSTALLED_DTBOIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ endif # BOARD_PREBUILT_DTBOIMAGE ifdef BOARD_KERNEL_PATH_16K $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(BUILT_KERNEL_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/ endif # BOARD_KERNEL_PATH_16K ifdef BOARD_KERNEL_MODULES_16K $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(BUILT_RAMDISK_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/ endif # BOARD_KERNEL_MODULES_16K ifdef BUILT_BOOTIMAGE_16K_TARGET $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(BUILT_BOOTIMAGE_16K_TARGET) $(zip_root)/IMAGES/ endif # BUILT_BOOTIMAGE_16K_TARGET ifdef INSTALLED_DTBOIMAGE_16KB_TARGET $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_DTBOIMAGE_16KB_TARGET) $(zip_root)/IMAGES/ endif # INSTALLED_DTBOIMAGE_16KB_TARGET ifeq ($(BOARD_USES_PVMFWIMAGE),true) $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/ $(hide) cp $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) $(zip_root)/PREBUILT_IMAGES/ $(hide) mkdir -p $(zip_root)/PVMFW $(hide) cp $(INSTALLED_PVMFW_BINARY_TARGET) $(zip_root)/PVMFW/ endif ifdef BOARD_PREBUILT_BOOTLOADER $(hide) mkdir -p $(zip_root)/IMAGES $(hide) cp $(INSTALLED_BOOTLOADER_MODULE) $(zip_root)/IMAGES/ endif ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),) $(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;)) endif # BOARD_CUSTOMIMAGES_PARTITION_LIST @# The radio images in BOARD_PACK_RADIOIMAGES will be additionally copied from RADIO/ into @# IMAGES/, which then will be added into -img.zip. Such images must be listed in @# INSTALLED_RADIOIMAGE_TARGET. $(hide) $(foreach part,$(BOARD_PACK_RADIOIMAGES), \ echo $(part) >> $(zip_root)/META/pack_radioimages.txt;) @# Run fs_config on all the system, vendor, boot ramdisk, @# and recovery ramdisk files in the zip, and save the output ifdef BUILDING_SYSTEM_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMIMAGE),$(zip_root)/IMAGES,system) $(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt endif ifdef BUILDING_VENDOR_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_VENDORIMAGE_TARGET),$(zip_root)/IMAGES,vendor) $(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt endif ifdef BUILDING_PRODUCT_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_PRODUCTIMAGE_TARGET),$(zip_root)/IMAGES,product) $(hide) $(call fs_config,$(zip_root)/PRODUCT,product/) > $(zip_root)/META/product_filesystem_config.txt endif ifdef BUILDING_SYSTEM_EXT_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_EXTIMAGE_TARGET),$(zip_root)/IMAGES,system_ext) $(hide) $(call fs_config,$(zip_root)/SYSTEM_EXT,system_ext/) > $(zip_root)/META/system_ext_filesystem_config.txt endif ifdef BUILDING_ODM_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_ODMIMAGE_TARGET),$(zip_root)/IMAGES,odm) $(hide) $(call fs_config,$(zip_root)/ODM,odm/) > $(zip_root)/META/odm_filesystem_config.txt endif ifdef BUILDING_VENDOR_DLKM_IMAGE $(hide)$(call copy-image-and-generate-map,$(BUILT_VENDOR_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,vendor_dlkm) $(hide) $(call fs_config,$(zip_root)/VENDOR_DLKM,vendor_dlkm/) > $(zip_root)/META/vendor_dlkm_filesystem_config.txt endif ifdef BUILDING_ODM_DLKM_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_ODM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,odm_dlkm) $(hide) $(call fs_config,$(zip_root)/ODM_DLKM,odm_dlkm/) > $(zip_root)/META/odm_dlkm_filesystem_config.txt endif ifdef BUILDING_SYSTEM_DLKM_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,system_dlkm) $(hide) $(call fs_config,$(zip_root)/SYSTEM_DLKM,system_dlkm/) > $(zip_root)/META/system_dlkm_filesystem_config.txt endif @# ROOT always contains the files for the root under normal boot. $(hide) $(call fs_config,$(zip_root)/ROOT,) > $(zip_root)/META/root_filesystem_config.txt @# BOOT/RAMDISK contains the first stage and recovery ramdisk. $(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt ifdef BUILDING_INIT_BOOT_IMAGE $(hide) $(call package_files-copy-root, $(TARGET_RAMDISK_OUT),$(zip_root)/INIT_BOOT/RAMDISK) $(hide) $(call fs_config,$(zip_root)/INIT_BOOT/RAMDISK,) > $(zip_root)/META/init_boot_filesystem_config.txt $(hide) cp $(RAMDISK_NODE_LIST) $(zip_root)/META/ramdisk_node_list ifdef BOARD_KERNEL_PAGESIZE $(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/INIT_BOOT/pagesize endif # BOARD_KERNEL_PAGESIZE endif # BUILDING_INIT_BOOT_IMAGE ifdef BOARD_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_EROFS_COMPRESS_HINTS) $(zip_root)/META/erofs_default_compress_hints.txt endif ifdef BOARD_SYSTEMIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_SYSTEMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_erofs_compress_hints.txt endif ifdef BOARD_SYSTEM_EXTIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_SYSTEM_EXTIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_ext_erofs_compress_hints.txt endif ifdef BOARD_PRODUCTIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_PRODUCTIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/product_erofs_compress_hints.txt endif ifdef BOARD_VENDORIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_VENDORIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/vendor_erofs_compress_hints.txt endif ifdef BOARD_ODMIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_ODMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/odm_erofs_compress_hints.txt endif ifdef BOARD_VENDOR_DLKMIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_VENDOR_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/vendor_dlkm_erofs_compress_hints.txt endif ifdef BOARD_ODM_DLKMIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_ODM_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/odm_dlkm_erofs_compress_hints.txt endif ifdef BOARD_SYSTEM_DLKMIMAGE_EROFS_COMPRESS_HINTS $(hide) cp $(BOARD_SYSTEM_DLKMIMAGE_EROFS_COMPRESS_HINTS) $(zip_root)/META/system_dlkm_erofs_compress_hints.txt endif ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),) $(call fs_config,$(zip_root)/VENDOR_BOOT/RAMDISK,) > $(zip_root)/META/vendor_boot_filesystem_config.txt endif ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt endif ifdef BUILDING_SYSTEM_OTHER_IMAGE $(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMOTHERIMAGE_TARGET),$(zip_root)/IMAGES) $(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt endif @# Metadata for compatibility verification. ifdef BUILT_KERNEL_CONFIGS_FILE $(hide) cp $(BUILT_KERNEL_CONFIGS_FILE) $(zip_root)/META/kernel_configs.txt endif ifdef BUILT_KERNEL_VERSION_FILE $(hide) cp $(BUILT_KERNEL_VERSION_FILE) $(zip_root)/META/kernel_version.txt endif rm -rf $(zip_root)/META/dynamic_partitions_info.txt ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) $(call dump-dynamic-partitions-info, $(zip_root)/META/dynamic_partitions_info.txt) endif PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \ $(ADD_IMG_TO_TARGET_FILES) -a -v -p $(HOST_OUT) $(zip_root) ifeq ($(BUILD_QEMU_IMAGES),true) $(hide) AVBTOOL=$(AVBTOOL) $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(zip_root)/IMAGES/vbmeta.img \ $(zip_root)/IMAGES/system.img $(zip_root)/IMAGES/VerifiedBootParams.textproto endif @# Zip everything up, preserving symlinks and placing META/ files first to @# help early validation of the .zip file while uploading it. $(hide) find $(zip_root)/META | sort >$@ $(hide) find $(zip_root) -path $(zip_root)/META -prune -o -print | sort >>$@ $(BUILT_TARGET_FILES_PACKAGE): $(BUILT_TARGET_FILES_DIR) @echo "Packaging target files: $@" $(hide) $(SOONG_ZIP) -d -o $@ -C $(zip_root) -r $@.list -sha256 .PHONY: target-files-package target-files-package: $(BUILT_TARGET_FILES_PACKAGE) .PHONY: target-files-dir target-files-dir: $(BUILT_TARGET_FILES_DIR) $(call declare-1p-container,$(BUILT_TARGET_FILES_PACKAGE),) $(call declare-container-license-deps,$(BUILT_TARGET_FILES_PACKAGE), $(INSTALLED_RADIOIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_DTBOIMAGE_TARGET) \ $(INSTALLED_PVMFWIMAGE_TARGET) \ $(INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET) \ $(INSTALLED_CUSTOMIMAGES_TARGET) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ $(INSTALLED_KERNEL_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_DTBIMAGE_TARGET) \ $(INSTALLED_2NDBOOTLOADER_TARGET) \ $(BOARD_PREBUILT_DTBOIMAGE) \ $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \ $(BOARD_RECOVERY_ACPIO) \ $(PRODUCT_SYSTEM_BASE_FS_PATH) \ $(PRODUCT_VENDOR_BASE_FS_PATH) \ $(PRODUCT_PRODUCT_BASE_FS_PATH) \ $(PRODUCT_SYSTEM_EXT_BASE_FS_PATH) \ $(PRODUCT_ODM_BASE_FS_PATH) \ $(PRODUCT_VENDOR_DLKM_BASE_FS_PATH) \ $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \ $(PRODUCT_SYSTEM_DLKM_BASE_FS_PATH) \ $(LPMAKE) \ $(SELINUX_FC) \ $(INSTALLED_MISC_INFO_TARGET) \ $(INSTALLED_FASTBOOT_INFO_TARGET) \ $(APKCERTS_FILE) \ $(APEX_KEYS_FILE) \ $(HOST_OUT_EXECUTABLES)/fs_config \ $(HOST_OUT_EXECUTABLES)/map_file_generator \ $(ADD_IMG_TO_TARGET_FILES) \ $(MAKE_RECOVERY_PATCH) \ $(BUILT_KERNEL_CONFIGS_FILE) \ $(BUILT_KERNEL_VERSION_FILE),$(BUILT_TARGET_FILES_PACKAGE):) $(call dist-for-goals-with-filenametag, target-files-package, $(BUILT_TARGET_FILES_PACKAGE)) # ----------------------------------------------------------------- # NDK Sysroot Package NDK_SYSROOT_TARGET := $(PRODUCT_OUT)/ndk_sysroot.tar.bz2 .PHONY: ndk_sysroot ndk_sysroot: $(NDK_SYSROOT_TARGET) $(NDK_SYSROOT_TARGET): $(SOONG_OUT_DIR)/ndk.timestamp @echo Package NDK sysroot... $(hide) tar cjf $@ -C $(SOONG_OUT_DIR) ndk ifeq ($(HOST_OS),linux) $(call dist-for-goals,sdk ndk_sysroot,$(NDK_SYSROOT_TARGET)) endif ifeq ($(build_ota_package),true) # ----------------------------------------------------------------- # OTA update package # $(1): output file # $(2): additional args define build-ota-package-target PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \ $(OTA_FROM_TARGET_FILES) \ --verbose \ --path $(HOST_OUT) \ $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \ $(if $(BOOT_VAR_OTA_CONFIG), --boot_variable_file $(BOOT_VAR_OTA_CONFIG)) \ $(2) \ $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1) endef product_name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) product_name := $(product_name)_debug endif name := $(product_name)-ota INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata $(call declare-0p-target,$(INTERNAL_OTA_METADATA)) $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) $(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA) $(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES) @echo "Package OTA: $@" $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA)) $(call declare-1p-container,$(INTERNAL_OTA_PACKAGE_TARGET),) $(call declare-container-license-deps,$(INTERNAL_OTA_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) .PHONY: otapackage otapackage: $(INTERNAL_OTA_PACKAGE_TARGET) ifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true) name := $(product_name)-ota-retrofit INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): \ $(BUILT_TARGET_FILES_PACKAGE) \ $(OTA_FROM_TARGET_FILES) \ $(INTERNAL_OTATOOLS_FILES) @echo "Package OTA (retrofit dynamic partitions): $@" $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --retrofit_dynamic_partitions) $(call declare-1p-container,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),) $(call declare-container-license-deps,$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) .PHONY: otardppackage otapackage otardppackage: $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) endif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),) name := $(product_name)-partial-ota INTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES) @echo "Package partial OTA: $@" $(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --partial "$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)") $(call declare-1p-container,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),) $(call declare-container-license-deps,$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES),$(PRODUCT_OUT)/:/) .PHONY: partialotapackage partialotapackage: $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) endif # BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST endif # build_ota_package # ----------------------------------------------------------------- # A zip of the appcompat directory containing logs APPCOMPAT_ZIP := $(PRODUCT_OUT)/appcompat.zip # For apps_only build we'll establish the dependency later in build/make/core/main.mk. ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(APPCOMPAT_ZIP): $(FULL_SYSTEMIMAGE_DEPS) \ $(INTERNAL_RAMDISK_FILES) \ $(INTERNAL_USERDATAIMAGE_FILES) \ $(INTERNAL_VENDORIMAGE_FILES) \ $(INTERNAL_PRODUCTIMAGE_FILES) \ $(INTERNAL_SYSTEM_EXTIMAGE_FILES) endif $(APPCOMPAT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,appcompat)/filelist $(APPCOMPAT_ZIP): $(SOONG_ZIP) @echo "appcompat logs: $@" $(hide) rm -rf $@ $(PRIVATE_LIST_FILE) $(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/appcompat $(dir $(PRIVATE_LIST_FILE)) $(hide) find $(PRODUCT_OUT)/appcompat | sort >$(PRIVATE_LIST_FILE) $(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/appcompat -l $(PRIVATE_LIST_FILE) DEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip $(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(DEXPREOPT_CONFIG_ZIP): $(DEX_PREOPT_CONFIG_FOR_MAKE) \ $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) \ endif $(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS := ifeq (,$(TARGET_BUILD_UNBUNDLED)) ifneq (,$(DEX_PREOPT_CONFIG_FOR_MAKE)) $(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS += -e $(notdir $(DEX_PREOPT_CONFIG_FOR_MAKE)) -f $(DEX_PREOPT_CONFIG_FOR_MAKE) endif ifneq (,$(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)) $(DEXPREOPT_CONFIG_ZIP): PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS += -e $(notdir $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)) -f $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) endif endif #!TARGET_BUILD_UNBUNDLED $(DEXPREOPT_CONFIG_ZIP): $(SOONG_ZIP) $(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/dexpreopt_config $(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/dexpreopt_config -D $(PRODUCT_OUT)/dexpreopt_config $(PRIVATE_DEXPREOPT_CONFIG_ZIP_PARAMS) .PHONY: dexpreopt_config_zip dexpreopt_config_zip: $(DEXPREOPT_CONFIG_ZIP) $(call declare-1p-target,$(DEXPREOPT_CONFIG_ZIP),) # ----------------------------------------------------------------- # Zips of the symbols directory per test suites # $(foreach suite,$(ALL_COMPATIBILITY_SUITES),$(eval $(call create-suite-symbols-map,$(suite)))) # ----------------------------------------------------------------- # A zip of the symbols directory. Keep the full paths to make it # more obvious where these files came from. # Also produces a textproto containing mappings from elf IDs to symbols # filename, which will allow finding the appropriate symbols to deobfuscate # a stack trace frame. # name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif # The path to the zip file containing binaries with symbols. SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name)-symbols.zip # The path to a file containing mappings from elf IDs to filenames. SYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping.textproto .KATI_READONLY := SYMBOLS_ZIP SYMBOLS_MAPPING ifeq (,$(TARGET_BUILD_UNBUNDLED)) _symbols_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT)) $(SYMBOLS_ZIP): $(updater_dep) else _symbols_zip_modules := $(unbundled_build_modules) endif _symbols_zip_modules_symbols_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).SYMBOLIC_OUTPUT_PATH)) _symbols_zip_modules_mapping_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).ELF_SYMBOL_MAPPING_PATH)) $(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_FILES := $(_symbols_zip_modules_symbols_files) $(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_MAPPING_FILES := $(_symbols_zip_modules_mapping_files) $(SYMBOLS_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP) $(_symbols_zip_modules_symbols_files) $(_symbols_zip_modules_mapping_files) @echo "Package symbols: $@" $(hide) rm -rf $@ $@.symbols_list $@.mapping_list # Find all installed files in the symbols directory and zip them into the symbols zip. echo "$(PRIVATE_SYMBOLS_MODULES_FILES)" | tr " " "\n" | sort > $@.symbols_list $(hide) $(SOONG_ZIP) -d -o $@ -l $@.symbols_list # Find all installed files in the symbols mapping directory and merge them into the symbols mapping textproto. echo "$(PRIVATE_SYMBOLS_MODULES_MAPPING_FILES)" | tr " " "\n" | sort > $@.mapping_list $(hide) $(SYMBOLS_MAP) -merge $(SYMBOLS_MAPPING) @$@.mapping_list $(SYMBOLS_ZIP): .KATI_IMPLICIT_OUTPUTS := $(SYMBOLS_MAPPING) $(call declare-1p-container,$(SYMBOLS_ZIP),) ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(call declare-container-license-deps,$(SYMBOLS_ZIP),$(PRIVATE_SYMBOLS_MODULES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/) endif _symbols_zip_modules_symbols_files := _symbols_zip_modules_mapping_files := # ----------------------------------------------------------------- # A zip of the coverage directory. # name := gcov-report-files-all ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif COVERAGE_ZIP := $(PRODUCT_OUT)/$(name).zip ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(COVERAGE_ZIP): $(INTERNAL_ALLIMAGES_FILES) endif $(COVERAGE_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,coverage)/filelist $(COVERAGE_ZIP): $(SOONG_ZIP) @echo "Package coverage: $@" $(hide) rm -rf $@ $(PRIVATE_LIST_FILE) $(hide) mkdir -p $(dir $@) $(TARGET_OUT_COVERAGE) $(dir $(PRIVATE_LIST_FILE)) $(hide) find $(TARGET_OUT_COVERAGE) | sort >$(PRIVATE_LIST_FILE) $(hide) $(SOONG_ZIP) -d -o $@ -C $(TARGET_OUT_COVERAGE) -l $(PRIVATE_LIST_FILE) $(call declare-1p-container,$(COVERAGE_ZIP),) ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(call declare-container-license-deps,$(COVERAGE_ZIP),$(INTERNAL_ALLIMAGE_FILES),$(PRODUCT_OUT)/:/) endif SYSTEM_NOTICE_DEPS += $(COVERAGE_ZIP) #------------------------------------------------------------------ # Export the LLVM profile data tool and dependencies for Clang coverage processing # ifeq (true,$(CLANG_COVERAGE)) LLVM_PROFDATA := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-profdata LLVM_COV := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-cov LIBCXX := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/x86_64-unknown-linux-gnu/libc++.so # Use llvm-profdata.zip for backwards compatibility with tradefed code. LLVM_COVERAGE_TOOLS_ZIP := $(PRODUCT_OUT)/llvm-profdata.zip $(LLVM_COVERAGE_TOOLS_ZIP): $(SOONG_ZIP) $(hide) $(SOONG_ZIP) -d -o $@ -C $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION) -f $(LLVM_PROFDATA) -f $(LIBCXX) -f $(LLVM_COV) $(call dist-for-goals,droidcore-unbundled apps_only,$(LLVM_COVERAGE_TOOLS_ZIP)) endif ifeq (true,$(EMMA_INSTRUMENT)) #------------------------------------------------------------------ # An archive of classes for use in generating code-coverage reports # These are the uninstrumented versions of any classes that were # to be instrumented. # Any dependencies are set up later in build/make/core/main.mk. JACOCO_REPORT_CLASSES_ALL := $(PRODUCT_OUT)/jacoco-report-classes-all.jar $(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco) $(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_JACOCO_DIR := $(call intermediates-dir-for,PACKAGING,jacoco,HOST) $(JACOCO_REPORT_CLASSES_ALL): PRIVATE_TARGET_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage) $(JACOCO_REPORT_CLASSES_ALL): PRIVATE_HOST_PROGUARD_USAGE_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage,HOST) $(JACOCO_REPORT_CLASSES_ALL) : @echo "Collecting uninstrumented classes" mkdir -p $(PRIVATE_TARGET_JACOCO_DIR) $(PRIVATE_HOST_JACOCO_DIR) $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) $(PRIVATE_HOST_PROGUARD_USAGE_DIR) $(SOONG_ZIP) -o $@ -L 0 \ -C $(PRIVATE_TARGET_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_JACOCO_DIR) \ -C $(PRIVATE_HOST_JACOCO_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_JACOCO_DIR) \ -C $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_TARGET_PROGUARD_USAGE_DIR) \ -C $(PRIVATE_HOST_PROGUARD_USAGE_DIR) -P out/target/common/obj -D $(PRIVATE_HOST_PROGUARD_USAGE_DIR) ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(JACOCO_REPORT_CLASSES_ALL): $(INTERNAL_ALLIMAGES_FILES) endif # This is not ideal, but it is difficult to correctly figure out the actual jacoco report # jars we need to add here as dependencies, so we add the device-tests as a dependency when # the env variable is set and this should guarantee thaat all the jacoco report jars are ready # when we package the final report jar here. ifeq ($(JACOCO_PACKAGING_INCLUDE_DEVICE_TESTS),true) $(JACOCO_REPORT_CLASSES_ALL): $(COMPATIBILITY.device-tests.FILES) endif endif # EMMA_INSTRUMENT=true #------------------------------------------------------------------ # A zip of Proguard obfuscation dictionary files. # Also produces a textproto containing mappings from the hashes of the # dictionary contents (which are also stored in the dex files on the # devices) to the filename of the proguard dictionary, which will allow # finding the appropriate dictionary to deobfuscate a stack trace frame. # ifeq (,$(TARGET_BUILD_UNBUNDLED)) _proguard_dict_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT)) else _proguard_dict_zip_modules := $(unbundled_build_modules) endif # Filter out list to avoid uncessary proguard related file generation ifeq (,$(TARGET_BUILD_UNBUNDLED)) filter_out_proguard_dict_zip_modules := # product.img ifndef BUILDING_PRODUCT_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/product/% endif # system.img ifndef BUILDING_SYSTEM_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system/% endif # system_dlkm.img ifndef BUILDING_SYSTEM_DLKM_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_dlkm/% endif # system_ext.img ifndef BUILDING_SYSTEM_EXT_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_ext/% endif # system_other.img ifndef BUILDING_SYSTEM_OTHER_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_other/% endif # odm.img ifndef BUILDING_ODM_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm/% endif # odm_dlkm.img ifndef BUILDING_ODM_DLKM_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm_dlkm/% endif # vendor.img ifndef BUILDING_VENDOR_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor/% endif # vendor_dlkm.img ifndef BUILDING_VENDOR_DLKM_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor_dlkm/% endif # cache.img ifndef BUILDING_CACHE_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/cache/% endif # ramdisk.img ifndef BUILDING_RAMDISK_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/ramdisk/% endif # recovery.img ifndef INSTALLED_RECOVERYIMAGE_TARGET filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/recovery/% endif # userdata.img ifndef BUILDING_USERDATA_IMAGE filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/data/% endif # Check the installed files of each module and return the module name # or return empty if none of the files remain to be installed define filter-out-proguard-modules $(if $(filter-out $(filter_out_proguard_dict_zip_modules),$(call module-installed-files,$(1))),$(1)) endef # Filter out proguard dict zip modules those are not installed at the built image _proguard_dict_zip_modules := $(foreach m,$(_proguard_dict_zip_modules),$(strip $(call filter-out-proguard-modules,$(m)))) endif # The path to the zip file containing proguard dictionaries. PROGUARD_DICT_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip $(PROGUARD_DICT_ZIP): PRIVATE_SOONG_ZIP_ARGUMENTS := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS)) $(PROGUARD_DICT_ZIP): $(SOONG_ZIP) $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_FILES)) @echo "Packaging Proguard obfuscation dictionary files." # Zip all of the files in PROGUARD_DICTIONARY_FILES. echo -n > $@.tmparglist $(foreach arg,$(PRIVATE_SOONG_ZIP_ARGUMENTS),printf "%s\n" "$(arg)" >> $@.tmparglist$(newline)) $(SOONG_ZIP) -d -o $@ @$@.tmparglist rm -f $@.tmparglist # The path to the zip file containing mappings from dictionary hashes to filenames. PROGUARD_DICT_MAPPING :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-mapping.textproto _proguard_dict_mapping_files := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_MAPPING)) $(PROGUARD_DICT_MAPPING): PRIVATE_MAPPING_FILES := $(_proguard_dict_mapping_files) $(PROGUARD_DICT_MAPPING): $(SYMBOLS_MAP) $(_proguard_dict_mapping_files) @echo "Packaging Proguard obfuscation dictionary mapping files." # Merge all the mapping files together echo -n > $@.tmparglist $(foreach mf,$(PRIVATE_MAPPING_FILES),echo "$(mf)" >> $@.tmparglist$(newline)) $(SYMBOLS_MAP) -merge $(PROGUARD_DICT_MAPPING) @$@.tmparglist rm -f $@.tmparglist $(call declare-1p-container,$(PROGUARD_DICT_ZIP),) ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(INTERNAL_ALLIMAGES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/) endif #------------------------------------------------------------------ # A zip of Proguard usage files. # PROGUARD_USAGE_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage.zip _proguard_usage_zips := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_USAGE_ZIP)) $(PROGUARD_USAGE_ZIP): PRIVATE_ZIPS := $(_proguard_usage_zips) $(PROGUARD_USAGE_ZIP): $(MERGE_ZIPS) $(_proguard_usage_zips) @echo "Packaging Proguard usage files." echo -n > $@.tmparglist $(foreach z,$(PRIVATE_ZIPS),echo "$(z)" >> $@.tmparglist$(newline)) $(MERGE_ZIPS) $@ @$@.tmparglist rm -rf $@.tmparglist _proguard_dict_mapping_files := _proguard_usage_zips := _proguard_dict_zip_modules := $(call declare-1p-container,$(PROGUARD_USAGE_ZIP),) ifeq (,$(TARGET_BUILD_UNBUNDLED)) $(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ $(updater_dep),$(PROGUARD_USAGE_ZIP):/) endif ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) # Dump variables used by build_super_image.py (for building super.img and super_empty.img). # $(1): output file define dump-super-image-info $(call dump-dynamic-partitions-info,$(1)) $(if $(filter true,$(AB_OTA_UPDATER)), \ echo "ab_update=true" >> $(1)) endef endif # PRODUCT_USE_DYNAMIC_PARTITIONS # ----------------------------------------------------------------- # super partition image (dist) ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) # BOARD_SUPER_PARTITION_SIZE must be defined to build super image. ifneq ($(BOARD_SUPER_PARTITION_SIZE),) ifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)) # For real devices and for dist builds, build super image from target files to an intermediate directory. INTERNAL_SUPERIMAGE_DIST_TARGET := $(call intermediates-dir-for,PACKAGING,super.img)/super.img $(INTERNAL_SUPERIMAGE_DIST_TARGET): extracted_input_target_files := $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(INTERNAL_SUPERIMAGE_DIST_TARGET): $(LPMAKE) $(BUILT_TARGET_FILES_DIR) $(BUILD_SUPER_IMAGE) $(call pretty,"Target super fs image from target files: $@") PATH=$(dir $(LPMAKE)):$$PATH \ $(BUILD_SUPER_IMAGE) -v $(extracted_input_target_files) $@ # Skip packing it in dist package because it is in update package. ifneq (true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)) $(call dist-for-goals,dist_files,$(INTERNAL_SUPERIMAGE_DIST_TARGET)) endif .PHONY: superimage_dist superimage_dist: $(INTERNAL_SUPERIMAGE_DIST_TARGET) endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != "true" endif # BOARD_SUPER_PARTITION_SIZE != "" endif # PRODUCT_BUILD_SUPER_PARTITION == "true" # ----------------------------------------------------------------- # super partition image for development ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION)) ifneq ($(BOARD_SUPER_PARTITION_SIZE),) ifneq (true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)) # Build super.img by using $(INSTALLED_*IMAGE_TARGET) to $(1) # $(1): built image path # $(2): misc_info.txt path; its contents should match expectation of build_super_image.py define build-superimage-target mkdir -p $(dir $(2)) rm -rf $(2) $(call dump-super-image-info,$(2)) $(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \ echo "$(p)_image=$(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)" >> $(2);) $(if $(BUILDING_SYSTEM_OTHER_IMAGE), $(if $(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)), \ echo "system_other_image=$(INSTALLED_SYSTEMOTHERIMAGE_TARGET)" >> $(2);)) mkdir -p $(dir $(1)) PATH=$(dir $(LPMAKE)):$$PATH \ $(BUILD_SUPER_IMAGE) -v $(2) $(1) endef INSTALLED_SUPERIMAGE_TARGET := $(PRODUCT_OUT)/super.img INSTALLED_SUPERIMAGE_DEPENDENCIES := $(LPMAKE) $(BUILD_SUPER_IMAGE) \ $(foreach p, $(BOARD_SUPER_PARTITION_PARTITION_LIST), $(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)) ifdef BUILDING_SYSTEM_OTHER_IMAGE ifneq ($(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)),) INSTALLED_SUPERIMAGE_DEPENDENCIES += $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) endif endif $(INSTALLED_SUPERIMAGE_TARGET): $(INSTALLED_SUPERIMAGE_DEPENDENCIES) $(call pretty,"Target super fs image for debug: $@") $(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\ $(call intermediates-dir-for,PACKAGING,superimage_debug)/misc_info.txt) # For devices that uses super image directly, the superimage target points to the file in $(PRODUCT_OUT). .PHONY: superimage superimage: $(INSTALLED_SUPERIMAGE_TARGET) # If BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT is set, super.img is built from images in the # $(PRODUCT_OUT) directory, and is built to $(PRODUCT_OUT)/super.img. Also, it will # be built for non-dist builds. This is useful for devices that uses super.img directly, e.g. # virtual devices. ifeq (true,$(BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT)) droidcore-unbundled: $(INSTALLED_SUPERIMAGE_TARGET) $(call dist-for-goals,dist_files,$(INSTALLED_MISC_INFO_TARGET):super_misc_info.txt) endif # BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT # Build $(PRODUCT_OUT)/super.img without dependencies. .PHONY: superimage-nodeps supernod superimage-nodeps supernod: intermediates := superimage-nodeps supernod: | $(INSTALLED_SUPERIMAGE_DEPENDENCIES) $(call pretty,"make $(INSTALLED_SUPERIMAGE_TARGET): ignoring dependencies") $(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\ $(call intermediates-dir-for,PACKAGING,superimage-nodeps)/misc_info.txt) endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS != "true" endif # BOARD_SUPER_PARTITION_SIZE != "" endif # PRODUCT_BUILD_SUPER_PARTITION == "true" # ----------------------------------------------------------------- # super empty image ifdef BUILDING_SUPER_EMPTY_IMAGE INSTALLED_SUPERIMAGE_EMPTY_TARGET := $(PRODUCT_OUT)/super_empty.img $(INSTALLED_SUPERIMAGE_EMPTY_TARGET): intermediates := $(call intermediates-dir-for,PACKAGING,super_empty) $(INSTALLED_SUPERIMAGE_EMPTY_TARGET): $(LPMAKE) $(BUILD_SUPER_IMAGE) $(call pretty,"Target empty super fs image: $@") mkdir -p $(intermediates) rm -rf $(intermediates)/misc_info.txt $(call dump-super-image-info,$(intermediates)/misc_info.txt) PATH=$(dir $(LPMAKE)):$$PATH \ $(BUILD_SUPER_IMAGE) -v $(intermediates)/misc_info.txt $@ $(call dist-for-goals,dist_files,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET)) $(call declare-0p-target,$(INSTALLED_SUPERIMAGE_EMPTY_TARGET)) endif # BUILDING_SUPER_EMPTY_IMAGE # ----------------------------------------------------------------- # The update package name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif name := $(name)-img INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip $(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES) $(call pretty,"Package: $@") PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \ $(IMG_FROM_TARGET_FILES) \ --additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto \ $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \ $(if $(BOARD_$(call to-upper,$(partition))_IMAGE_NO_FLASHALL), \ --exclude IMAGES/$(partition).img \ --exclude IMAGES/$(partition).map \ ) \ ) \ --build_super_image $(BUILD_SUPER_IMAGE) \ $(BUILT_TARGET_FILES_PACKAGE) $@ $(call declare-1p-container,$(INTERNAL_UPDATE_PACKAGE_TARGET),) $(call declare-container-license-deps,$(INTERNAL_UPDATE_PACKAGE_TARGET),$(BUILT_TARGET_FILES_PACKAGE) $(IMG_FROM_TARGET_FILES),$(PRODUCT_OUT)/:/) .PHONY: updatepackage updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET) $(call dist-for-goals-with-filenametag,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET)) # ----------------------------------------------------------------- # dalvik something .PHONY: dalvikfiles dalvikfiles: $(INTERNAL_DALVIK_MODULES) ifeq ($(BUILD_QEMU_IMAGES),true) MK_QEMU_IMAGE_SH := device/generic/goldfish/tools/mk_qemu_image.sh MK_COMBINE_QEMU_IMAGE := $(HOST_OUT_EXECUTABLES)/mk_combined_img SGDISK_HOST := $(HOST_OUT_EXECUTABLES)/sgdisk ifdef INSTALLED_SYSTEMIMAGE_TARGET INSTALLED_QEMU_SYSTEMIMAGE := $(PRODUCT_OUT)/system-qemu.img INSTALLED_SYSTEM_QEMU_CONFIG := $(PRODUCT_OUT)/system-qemu-config.txt $(INSTALLED_SYSTEM_QEMU_CONFIG): $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_VBMETAIMAGE_TARGET) @echo "$(PRODUCT_OUT)/vbmeta.img vbmeta 1" > $@ @echo "$(INSTALLED_SUPERIMAGE_TARGET) super 2" >> $@ $(INSTALLED_QEMU_SYSTEMIMAGE): $(INSTALLED_VBMETAIMAGE_TARGET) $(MK_COMBINE_QEMU_IMAGE) $(SGDISK_HOST) $(SIMG2IMG) \ $(INSTALLED_SUPERIMAGE_TARGET) $(INSTALLED_SYSTEM_QEMU_CONFIG) @echo Create system-qemu.img now (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); \ $(MK_COMBINE_QEMU_IMAGE) -i $(INSTALLED_SYSTEM_QEMU_CONFIG) -o $@) systemimage: $(INSTALLED_QEMU_SYSTEMIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_SYSTEMIMAGE) endif ifdef INSTALLED_VENDORIMAGE_TARGET INSTALLED_QEMU_VENDORIMAGE := $(PRODUCT_OUT)/vendor-qemu.img $(INSTALLED_QEMU_VENDORIMAGE): $(INSTALLED_VENDORIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) @echo Create vendor-qemu.img (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDORIMAGE_TARGET)) vendorimage: $(INSTALLED_QEMU_VENDORIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_VENDORIMAGE) endif ifdef INSTALLED_RAMDISK_TARGET ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET ifdef INTERNAL_VENDOR_RAMDISK_TARGET INSTALLED_QEMU_RAMDISKIMAGE := $(PRODUCT_OUT)/ramdisk-qemu.img $(INSTALLED_QEMU_RAMDISKIMAGE): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_RAMDISK_TARGET) @echo Create ramdisk-qemu.img (cat $(INSTALLED_RAMDISK_TARGET) $(INTERNAL_VENDOR_RAMDISK_TARGET) > $(INSTALLED_QEMU_RAMDISKIMAGE)) droidcore-unbundled: $(INSTALLED_QEMU_RAMDISKIMAGE) endif endif endif ifdef INSTALLED_PRODUCTIMAGE_TARGET INSTALLED_QEMU_PRODUCTIMAGE := $(PRODUCT_OUT)/product-qemu.img $(INSTALLED_QEMU_PRODUCTIMAGE): $(INSTALLED_PRODUCTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) @echo Create product-qemu.img (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_PRODUCTIMAGE_TARGET)) productimage: $(INSTALLED_QEMU_PRODUCTIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_PRODUCTIMAGE) endif ifdef INSTALLED_SYSTEM_EXTIMAGE_TARGET INSTALLED_QEMU_SYSTEM_EXTIMAGE := $(PRODUCT_OUT)/system_ext-qemu.img $(INSTALLED_QEMU_SYSTEM_EXTIMAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) $(SIMG2IMG) @echo Create system_ext-qemu.img (export SGDISK=$(SGDISK_HOST) SIMG2IMG=$(SIMG2IMG); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)) systemextimage: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_EXTIMAGE) endif ifdef INSTALLED_ODMIMAGE_TARGET INSTALLED_QEMU_ODMIMAGE := $(PRODUCT_OUT)/odm-qemu.img $(INSTALLED_QEMU_ODMIMAGE): $(INSTALLED_ODMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) @echo Create odm-qemu.img (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODMIMAGE_TARGET)) odmimage: $(INSTALLED_QEMU_ODMIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_ODMIMAGE) endif ifdef INSTALLED_VENDOR_DLKMIMAGE_TARGET INSTALLED_QEMU_VENDOR_DLKMIMAGE := $(PRODUCT_OUT)/vendor_dlkm-qemu.img $(INSTALLED_QEMU_VENDOR_DLKMIMAGE): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) @echo Create vendor_dlkm-qemu.img (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)) vendor_dlkmimage: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_VENDOR_DLKMIMAGE) endif ifdef INSTALLED_ODM_DLKMIMAGE_TARGET INSTALLED_QEMU_ODM_DLKMIMAGE := $(PRODUCT_OUT)/odm_dlkm-qemu.img $(INSTALLED_QEMU_ODM_DLKMIMAGE): $(INSTALLED_ODM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) @echo Create odm_dlkm-qemu.img (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_ODM_DLKMIMAGE_TARGET)) odm_dlkmimage: $(INSTALLED_QEMU_ODM_DLKMIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_ODM_DLKMIMAGE) endif ifdef INSTALLED_SYSTEM_DLKMIMAGE_TARGET INSTALLED_QEMU_SYSTEM_DLKMIMAGE := $(PRODUCT_OUT)/system_dlkm-qemu.img $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST) @echo Create system_dlkm-qemu.img (export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)) system_dlkmimage: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE) droidcore-unbundled: $(INSTALLED_QEMU_SYSTEM_DLKMIMAGE) endif QEMU_VERIFIED_BOOT_PARAMS := $(PRODUCT_OUT)/VerifiedBootParams.textproto $(QEMU_VERIFIED_BOOT_PARAMS): $(INSTALLED_VBMETAIMAGE_TARGET) $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(AVBTOOL) @echo Creating $@ (export AVBTOOL=$(AVBTOOL); $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(INSTALLED_VBMETAIMAGE_TARGET) \ $(INSTALLED_SYSTEMIMAGE_TARGET) $(QEMU_VERIFIED_BOOT_PARAMS)) systemimage: $(QEMU_VERIFIED_BOOT_PARAMS) droidcore-unbundled: $(QEMU_VERIFIED_BOOT_PARAMS) endif # Preprocess files for emulator and sdk. -include development/build/tools/sdk-preprocess-files.mk # ----------------------------------------------------------------- # The emulator package ifeq ($(BUILD_EMULATOR),true) INTERNAL_EMULATOR_PACKAGE_FILES += \ $(HOST_OUT_EXECUTABLES)/emulator$(HOST_EXECUTABLE_SUFFIX) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) name := $(TARGET_PRODUCT)-emulator INTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip $(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES) @echo "Package: $@" $(hide) zip -qjX $@ $(INTERNAL_EMULATOR_PACKAGE_FILES) endif # ----------------------------------------------------------------- # The SDK ifneq ($(filter sdk,$(MAKECMDGOALS)),) # The SDK includes host-specific components, so it belongs under HOST_OUT. sdk_dir := $(HOST_OUT)/sdk/$(TARGET_PRODUCT) # Build a name that looks like: # # linux-x86 --> android-sdk_12345_linux-x86 # darwin-x86 --> android-sdk_12345_mac-x86 # windows-x86 --> android-sdk_12345_windows # ifneq ($(HOST_OS),linux) $(error Building the monolithic SDK is only supported on Linux) endif sdk_name := android-sdk INTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH) sdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME) sdk_dep_file := $(sdk_dir)/sdk_deps.mk ATREE_FILES := -include $(sdk_dep_file) # if we don't have a real list, then use "everything" ifeq ($(strip $(ATREE_FILES)),) ATREE_FILES := \ $(ALL_DOCS) \ $(ALL_SDK_FILES) endif atree_dir := development/build sdk_atree_files := $(atree_dir)/sdk.exclude.atree # development/build/sdk-android-.atree is used to differentiate # between architecture models (e.g. ARMv5TE versus ARMv7) when copying # files like the kernel image. We use TARGET_CPU_ABI because we don't # have a better way to distinguish between CPU models. ifneq (,$(strip $(wildcard $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree))) sdk_atree_files += $(atree_dir)/sdk-android-$(TARGET_CPU_ABI).atree endif ifneq ($(PRODUCT_SDK_ATREE_FILES),) sdk_atree_files += $(PRODUCT_SDK_ATREE_FILES) else sdk_atree_files += $(atree_dir)/sdk.atree endif SDK_METADATA_DIR :=$= $(call intermediates-dir-for,PACKAGING,framework-doc-stubs-metadata,,COMMON) SDK_METADATA_FILES :=$= $(addprefix $(SDK_METADATA_DIR)/,\ activity_actions.txt \ broadcast_actions.txt \ categories.txt \ features.txt \ service_actions.txt \ widgets.txt) SDK_METADATA :=$= $(firstword $(SDK_METADATA_FILES)) $(SDK_METADATA): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(SDK_METADATA),$(SDK_METADATA_FILES)) $(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip rm -rf $(SDK_METADATA_DIR) mkdir -p $(SDK_METADATA_DIR) unzip -DDqo $< -d $(SDK_METADATA_DIR) .PHONY: framework-doc-stubs framework-doc-stubs: $(SDK_METADATA) deps := \ $(OUT_DOCS)/offline-sdk-timestamp \ $(SDK_METADATA_FILES) \ $(INSTALLED_SDK_BUILD_PROP_TARGET) \ $(ATREE_FILES) \ $(sdk_atree_files) \ $(HOST_OUT_EXECUTABLES)/atree \ $(HOST_OUT_EXECUTABLES)/line_endings # The name of the subdir within the platforms dir of the sdk. One of: # - android- (stable base dessert SDKs) # - android- (stable extension SDKs) # - android--ext (codename SDKs) sdk_platform_dir_name := $(strip \ $(if $(filter REL,$(PLATFORM_VERSION_CODENAME)), \ $(if $(filter $(PLATFORM_SDK_EXTENSION_VERSION),$(PLATFORM_BASE_SDK_EXTENSION_VERSION)), \ android-$(PLATFORM_SDK_VERSION), \ android-$(PLATFORM_SDK_VERSION)-ext$(PLATFORM_SDK_EXTENSION_VERSION) \ ), \ android-$(PLATFORM_VERSION_CODENAME) \ ) \ ) INTERNAL_SDK_TARGET := $(sdk_dir)/$(sdk_name).zip $(INTERNAL_SDK_TARGET): PRIVATE_NAME := $(sdk_name) $(INTERNAL_SDK_TARGET): PRIVATE_DIR := $(sdk_dir)/$(sdk_name) $(INTERNAL_SDK_TARGET): PRIVATE_DEP_FILE := $(sdk_dep_file) $(INTERNAL_SDK_TARGET): PRIVATE_INPUT_FILES := $(sdk_atree_files) $(INTERNAL_SDK_TARGET): PRIVATE_PLATFORM_NAME := $(sdk_platform_dir_name) # Set SDK_GNU_ERROR to non-empty to fail when a GNU target is built. # #SDK_GNU_ERROR := true $(INTERNAL_SDK_TARGET): $(deps) @echo "Package SDK: $@" $(hide) rm -rf $(PRIVATE_DIR) $@ $(hide) for f in $(strip $(target_gnu_MODULES)); do \ if [ -f $$f ]; then \ echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \ including GNU target $$f >&2; \ FAIL=$(SDK_GNU_ERROR); \ fi; \ done; \ if [ $$FAIL ]; then exit 1; fi $(hide) ( \ ATREE_STRIP="$(HOST_STRIP) -x" \ $(HOST_OUT_EXECUTABLES)/atree \ $(addprefix -f ,$(PRIVATE_INPUT_FILES)) \ -m $(PRIVATE_DEP_FILE) \ -I . \ -I $(PRODUCT_OUT) \ -I $(HOST_OUT) \ -I $(TARGET_COMMON_OUT_ROOT) \ -v "PLATFORM_NAME=$(PRIVATE_PLATFORM_NAME)" \ -v "OUT_DIR=$(OUT_DIR)" \ -v "HOST_OUT=$(HOST_OUT)" \ -v "TARGET_ARCH=$(TARGET_ARCH)" \ -v "TARGET_CPU_ABI=$(TARGET_CPU_ABI)" \ -v "DLL_EXTENSION=$(HOST_SHLIB_SUFFIX)" \ -o $(PRIVATE_DIR) && \ HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \ development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \ chmod -R ug+rwX $(PRIVATE_DIR) && \ cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \ ) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 ) MAIN_SDK_DIR := $(sdk_dir) MAIN_SDK_ZIP := $(INTERNAL_SDK_TARGET) endif # sdk in MAKECMDGOALS # ----------------------------------------------------------------- # Findbugs INTERNAL_FINDBUGS_XML_TARGET := $(PRODUCT_OUT)/findbugs.xml INTERNAL_FINDBUGS_HTML_TARGET := $(PRODUCT_OUT)/findbugs.html $(INTERNAL_FINDBUGS_XML_TARGET): $(ALL_FINDBUGS_FILES) @echo UnionBugs: $@ $(hide) $(FINDBUGS_DIR)/unionBugs $(ALL_FINDBUGS_FILES) \ > $@ $(INTERNAL_FINDBUGS_HTML_TARGET): $(INTERNAL_FINDBUGS_XML_TARGET) @echo ConvertXmlToText: $@ $(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl \ $(INTERNAL_FINDBUGS_XML_TARGET) > $@ # ----------------------------------------------------------------- # Findbugs # ----------------------------------------------------------------- # These are some additional build tasks that need to be run. ifneq ($(dont_bother),true) include $(sort $(wildcard $(BUILD_SYSTEM)/tasks/*.mk)) -include $(sort $(wildcard vendor/*/build/tasks/*.mk)) -include $(sort $(wildcard device/*/build/tasks/*.mk)) -include $(sort $(wildcard product/*/build/tasks/*.mk)) # Also the project-specific tasks -include $(sort $(wildcard vendor/*/*/build/tasks/*.mk)) -include $(sort $(wildcard device/*/*/build/tasks/*.mk)) -include $(sort $(wildcard product/*/*/build/tasks/*.mk)) # Also add test specifc tasks include $(sort $(wildcard platform_testing/build/tasks/*.mk)) include $(sort $(wildcard test/vts/tools/build/tasks/*.mk)) endif include $(BUILD_SYSTEM)/product-graph.mk # ----------------------------------------------------------------- # Create SDK repository packages. Must be done after tasks/* since # we need the addon rules defined. ifneq ($(sdk_repo_goal),) include $(TOPDIR)development/build/tools/sdk_repo.mk endif # ----------------------------------------------------------------- # Soong generates the list of all shared libraries that are depended on by fuzz # targets. It saves this list as a source:destination pair to # FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS, where the source is the path to the # build of the unstripped shared library, and the destination is the # /data/fuzz/$ARCH/lib (for device) or /fuzz/$ARCH/lib (for host) directory # where fuzz target shared libraries are to be "reinstalled". The # copy-many-files below generates the rules to copy the unstripped shared # libraries to the device or host "reinstallation" directory. These rules are # depended on by each module in soong_cc_prebuilt.mk, where the module will have # a dependency on each shared library that it needs to be "reinstalled". FUZZ_SHARED_DEPS := $(call copy-many-files,$(strip $(FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS))) # ----------------------------------------------------------------- # The rule to build all fuzz targets for C++ and Rust, and package them. # Note: The packages are created in Soong, and in a perfect world, # we'd be able to create the phony rule there. But, if we want to # have dist goals for the fuzz target, we need to have the PHONY # target defined in make. MakeVarsContext.DistForGoal doesn't take # into account that a PHONY rule create by Soong won't be available # during make, and such will fail with `writing to readonly # directory`, because kati will see 'haiku' as being a file, not a # phony target. .PHONY: haiku haiku: $(SOONG_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_FUZZ_TARGETS) $(call dist-for-goals,haiku,$(SOONG_FUZZ_PACKAGING_ARCH_MODULES)) $(call dist-for-goals,haiku,$(PRODUCT_OUT)/module-info.json) .PHONY: haiku-java haiku-java: $(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_JAVA_FUZZ_TARGETS) $(call dist-for-goals,haiku-java,$(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES)) .PHONY: haiku-rust haiku-rust: $(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_RUST_FUZZ_TARGETS) $(call dist-for-goals,haiku-rust,$(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES)) $(call dist-for-goals,haiku-rust,$(PRODUCT_OUT)/module-info.json) .PHONY: haiku-presubmit haiku-presubmit: $(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_PRESUBMIT_FUZZ_TARGETS) $(call dist-for-goals,haiku-presubmit,$(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES)) # ----------------------------------------------------------------- # Extract additional data files used in Layoutlib include $(BUILD_SYSTEM)/layoutlib_data.mk # ----------------------------------------------------------------- # Desktop pack common variables. PACK_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_image UPDATE_PARTITION_SCRIPT := $(HOST_OUT_EXECUTABLES)/update-partition IMAGES := $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_SUPERIMAGE_TARGET) \ $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \ $(INSTALLED_VBMETAIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) # ----------------------------------------------------------------- # Desktop generated firmware filesystem. TARGET_PRODUCT_FW_IMAGE_PACKAGE := prebuilt-$(TARGET_PRODUCT)-firmware-image GENERATED_FW_IMAGE := $(PRODUCT_OUT)/product/etc/$(TARGET_PRODUCT)-firmware.img generated_fw_image_found := $(strip $(foreach pp,$(PRODUCT_PACKAGES),\ $(if $(findstring $(TARGET_PRODUCT_FW_IMAGE_PACKAGE),$(pp)),$(pp)))) ifneq (,$(generated_fw_image_found)) $(call dist-for-goals,dist_files,$(GENERATED_FW_IMAGE)) endif # ----------------------------------------------------------------- # Desktop pack image hook. ifneq (,$(strip $(PACK_DESKTOP_FILESYSTEM_IMAGES))) PACK_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_image.bin $(PACK_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT) $(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) --noarchive PACKED_IMAGE_ARCHIVE_TARGET := $(PACK_IMAGE_TARGET).gz $(PACKED_IMAGE_ARCHIVE_TARGET): $(PACK_IMAGE_TARGET) | $(GZIP) $(GZIP) -fk $(PACK_IMAGE_TARGET) $(call dist-for-goals,dist_files,$(PACKED_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-image pack-image: $(PACK_IMAGE_TARGET) endif # PACK_DESKTOP_FILESYSTEM_IMAGES # ----------------------------------------------------------------- # Desktop pack recovery image hook. ifeq ($(BOARD_USES_DESKTOP_RECOVERY_IMAGE),true) PACK_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_recovery_image.bin PACK_RECOVERY_IMAGE_ARGS := --noarchive --recovery ifneq (,$(strip $(PACK_RECOVERY_IMAGE_EXPERIMENTAL))) PACK_RECOVERY_IMAGE_ARGS += --experimental endif # PACK_RECOVERY_IMAGE_EXPERIMENTAL $(PACK_RECOVERY_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT) $(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) $(PACK_RECOVERY_IMAGE_ARGS) PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET := $(PACK_RECOVERY_IMAGE_TARGET).gz $(PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET): $(PACK_RECOVERY_IMAGE_TARGET) | $(GZIP) $(GZIP) -fk $(PACK_RECOVERY_IMAGE_TARGET) $(call dist-for-goals,dist_files,$(PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-recovery-image pack-recovery-image: $(PACK_RECOVERY_IMAGE_TARGET) RECOVERY_SWAP_KERNEL_TARGET := $(PRODUCT_OUT)/recovery-kernel-swap # Has swap kernel for insecure recovery image. ifeq ($(BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL),true) $(call dist-for-goals,dist_files,$(RECOVERY_SWAP_KERNEL_TARGET)) PACK_INSECURE_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_insecure_recovery_image.bin $(PACK_INSECURE_RECOVERY_IMAGE_TARGET): PRIVATE_SGDISK := $(HOST_OUT_EXECUTABLES)/sgdisk $(PACK_INSECURE_RECOVERY_IMAGE_TARGET): $(PACK_RECOVERY_IMAGE_TARGET) $(UPDATE_PARTITION_SCRIPT) $(RECOVERY_SWAP_KERNEL_TARGET) @cp -f $< $@ (export SGDISK=$(PRIVATE_SGDISK); $(UPDATE_PARTITION_SCRIPT) KERN-A $@ $(RECOVERY_SWAP_KERNEL_TARGET)) PACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET := $(PACK_INSECURE_RECOVERY_IMAGE_TARGET).gz $(PACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET): $(PACK_INSECURE_RECOVERY_IMAGE_TARGET) | $(GZIP) $(GZIP) -fk $(PACK_INSECURE_RECOVERY_IMAGE_TARGET) $(call dist-for-goals,dist_files,$(PACKED_INSECURE_RECOVERY_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-insecure-recovery-image pack-insecure-recovery-image: $(PACK_INSECURE_RECOVERY_IMAGE_TARGET) endif # BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL endif # BOARD_USES_DESKTOP_RECOVERY_IMAGE # ----------------------------------------------------------------- # Desktop pack update image hook. ifeq ($(BOARD_USES_DESKTOP_UPDATE_IMAGE),true) PACK_UPDATE_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_update_image.bin PACK_UPDATE_IMAGE_ARGS := --noarchive --update ifneq (,$(strip $(PACK_UPDATE_IMAGE_EXPERIMENTAL))) PACK_UPDATE_IMAGE_ARGS += --experimental endif # PACK_UPDATE_IMAGE_EXPERIMENTAL $(PACK_UPDATE_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT) $(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) $(PACK_UPDATE_IMAGE_ARGS) PACKED_UPDATE_IMAGE_ARCHIVE_TARGET := $(PACK_UPDATE_IMAGE_TARGET).gz $(PACKED_UPDATE_IMAGE_ARCHIVE_TARGET): $(PACK_UPDATE_IMAGE_TARGET) | $(GZIP) $(GZIP) -fk $(PACK_UPDATE_IMAGE_TARGET) $(call dist-for-goals,dist_files,$(PACKED_UPDATE_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-update-image pack-update-image: $(PACK_UPDATE_IMAGE_TARGET) endif # BOARD_USES_DESKTOP_UPDATE_IMAGE PACK_MIGRATION_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_migration_image # ----------------------------------------------------------------- # Desktop pack migration image hook. ifeq ($(ANDROID_DESKTOP_MIGRATION_IMAGE),true) PACK_MIGRATION_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_migration_image.bin $(PACK_MIGRATION_IMAGE_TARGET): $(IMAGES) $(PACK_MIGRATION_IMAGE_SCRIPT) $(PACK_MIGRATION_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) --noarchive PACKED_MIGRATION_IMAGE_ARCHIVE_TARGET := $(PACK_MIGRATION_IMAGE_TARGET).gz $(PACKED_MIGRATION_IMAGE_ARCHIVE_TARGET): $(PACK_MIGRATION_IMAGE_TARGET) | $(GZIP) $(GZIP) -fk $(PACK_MIGRATION_IMAGE_TARGET) $(call dist-for-goals,dist_files,$(PACKED_MIGRATION_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-migration-image pack-migration-image: $(PACK_MIGRATION_IMAGE_TARGET) endif # ANDROID_DESKTOP_MIGRATION_IMAGE ifdef SOONG_ONLY_ALL_IMAGES_ZIP allimages_soong_zip_args := allimages_deps := define include_image $(if $(1), \ $(eval allimages_soong_zip_args += -e $(notdir $(1)) -f $(1)) \ $(eval allimages_deps += $(1))) endef $(call include_image,$(INSTALLED_SUPERIMAGE_TARGET)) $(call include_image,$(INSTALLED_BOOTIMAGE_TARGET)) $(call include_image,$(INSTALLED_INIT_BOOT_IMAGE_TARGET)) $(call include_image,$(INSTALLED_VENDOR_BOOTIMAGE_TARGET)) $(call include_image,$(INSTALLED_USERDATAIMAGE_TARGET)) $(call include_image,$(INSTALLED_RECOVERYIMAGE_TARGET)) $(call include_image,$(INSTALLED_VBMETAIMAGE_TARGET)) $(call include_image,$(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET)) $(call include_image,$(INSTALLED_VBMETA_VENDORIMAGE_TARGET)) $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)), \ $(call include_image,$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET))) allimages_zip := $(PRODUCT_OUT)/all_images.zip $(allimages_zip): PRIVATE_SOONG_ZIP_ARGUMENTS := $(allimages_soong_zip_args) $(allimages_zip): $(SOONG_ZIP) $(allimages_deps) $(SOONG_ZIP) -o $@ $(PRIVATE_SOONG_ZIP_ARGUMENTS) .PHONY: soong_only_diff_test soong_only_diff_test: PRIVATE_ALLIMAGES_ZIP := $(allimages_zip) soong_only_diff_test: $(allimages_zip) $(SOONG_ONLY_ALL_IMAGES_ZIP) diff $(PRIVATE_ALLIMAGES_ZIP) $(SOONG_ONLY_ALL_IMAGES_ZIP) allimages_soong_zip_args := allimages_deps := allimages_zip := include_image := endif # ifdef SOONG_ONLY_ALL_IMAGES_ZIP # ----------------------------------------------------------------- # OS Licensing include $(BUILD_SYSTEM)/os_licensing.mk # When appending new code to this file, please insert above OS Licensing ================================================ FILE: core/OWNERS ================================================ # For global Proguard rules per-file proguard*.flags = jdduke@google.com # For version updates per-file version_defaults.mk = ankurbakshi@google.com,bkhalife@google.com,jainne@google.com,lokeshgoel@google.com,lubomir@google.com,pscovanner@google.com # For sdk extensions version updates per-file version_defaults.mk = amhk@google.com,gurpreetgs@google.com,mkhokhlova@google.com,robertogil@google.com # For Ravenwood test configs per-file ravenwood_test_config_template.xml =omakoto@google.com ================================================ FILE: core/WINPTHREADS_COPYING ================================================ Copyright (c) 2011 mingw-w64 project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* * Parts of this library are derived by: * * Posix Threads library for Microsoft Windows * * Use at own risk, there is no implied warranty to this code. * It uses undocumented features of Microsoft Windows that can change * at any time in the future. * * (C) 2010 Lockless Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Lockless Inc. nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ ================================================ FILE: core/aapt2.mk ================================================ ###################################### # Compile resource with AAPT2 # Input variables: # - full_android_manifest # - my_res_resources # - my_overlay_resources # - my_compiled_res_base_dir # - my_asset_dirs # - my_full_asset_paths # - my_res_package # - R_file_stamp # - proguard_options_file # - my_generated_res_dirs: Resources generated during the build process and we have to compile them in a single run of aapt2. # - my_generated_res_dirs_deps: the dependency to use for my_generated_res_dirs. # - my_generated_res_zips: Zip files containing resources # - my_apk_split_configs: The configurations for which to generate splits. # - built_apk_splits: The paths where AAPT should generate the splits. # # Output variables: # - my_res_resources_flat # - my_overlay_resources_flat # - my_generated_resources_flata # ###################################### # Compile all the resource files. my_res_resources_flat := \ $(foreach r, $(my_res_resources),\ $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\ $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\ $(o)) my_overlay_resources_flat := \ $(foreach r, $(my_overlay_resources),\ $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\ $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\ $(o)) my_resources_flata := # Compile generated resources ifneq ($(my_generated_res_dirs),) my_generated_resources_flata := $(my_compiled_res_base_dir)/gen_res.flata $(my_generated_resources_flata): PRIVATE_SOURCE_RES_DIRS := $(my_generated_res_dirs) $(my_generated_resources_flata) : $(my_generated_res_dirs_deps) $(AAPT2) @echo "AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_DIRS)" $(call aapt2-compile-resource-dirs) my_resources_flata += $(my_generated_resources_flata) endif # Compile zipped resources ifneq ($(my_generated_res_zips),) my_zipped_resources_flata := $(my_compiled_res_base_dir)/zip_res.flata $(my_zipped_resources_flata): PRIVATE_SOURCE_RES_ZIPS := $(my_generated_res_zips) $(my_zipped_resources_flata) : $(my_generated_res_zips) $(AAPT2) $(ZIPSYNC) @echo "AAPT2 compile $@ <- $(PRIVATE_SOURCE_RES_ZIPS)" $(call aapt2-compile-resource-zips) my_resources_flata += $(my_zipped_resources_flata) endif # Always set --pseudo-localize, it will be stripped out later for release # builds that don't want it. $(my_res_resources_flat) $(my_overlay_resources_flat) $(my_resources_flata) $(my_generated_resources_flata) $(my_zippped_resources_flata): \ PRIVATE_AAPT2_CFLAGS := --pseudo-localize $(filter --legacy,$(LOCAL_AAPT_FLAGS)) # TODO(b/78447299): Forbid LOCAL_STATIC_JAVA_AAR_LIBRARIES in aapt2 and remove # support for it. my_static_library_resources := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk) my_static_library_transitive_resource_packages_lists := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/transitive-res-packages) my_static_library_extra_packages := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/extra_packages) my_shared_library_resources := $(foreach l, $(LOCAL_SHARED_ANDROID_LIBRARIES),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk) ifneq ($(my_static_library_resources),) $(my_res_package): PRIVATE_AAPT_FLAGS += --auto-add-overlay endif ifneq ($(my_apk_split_configs),) # Join the Split APK paths with their configuration, separated by a ':'. $(my_res_package): PRIVATE_AAPT_FLAGS += $(addprefix --split ,$(join $(built_apk_splits),$(addprefix :,$(my_apk_split_configs)))) endif my_srcjar := $(intermediates.COMMON)/aapt2.srcjar LOCAL_SRCJARS += $(my_srcjar) aapt_extra_packages := $(intermediates.COMMON)/extra_packages $(my_res_package): PRIVATE_RES_FLAT := $(my_res_resources_flat) $(my_res_package): PRIVATE_OVERLAY_FLAT := $(my_static_library_resources) $(my_resources_flata) $(my_overlay_resources_flat) $(my_res_package): PRIVATE_SHARED_ANDROID_LIBRARIES := $(my_shared_library_resources) $(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file) $(my_res_package): PRIVATE_ASSET_DIRS := $(my_asset_dirs) $(my_res_package): PRIVATE_JAVA_GEN_DIR := $(intermediates.COMMON)/aapt2 $(my_res_package): PRIVATE_SRCJAR := $(my_srcjar) $(my_res_package): PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES := $(my_static_library_extra_packages) $(my_res_package): PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS := $(my_static_library_transitive_resource_packages_lists) $(my_res_package): PRIVATE_AAPT_EXTRA_PACKAGES := $(aapt_extra_packages) $(my_res_package): .KATI_IMPLICIT_OUTPUTS := $(my_srcjar) $(aapt_extra_packages) ifdef R_file_stamp $(my_res_package): PRIVATE_R_FILE_STAMP := $(R_file_stamp) $(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(R_file_stamp) endif resource_export_package := ifdef LOCAL_EXPORT_PACKAGE_RESOURCES # Put this module's resources into a PRODUCT-agnositc package that # other packages can use to build their own PRODUCT-agnostic R.java (etc.) # files. resource_export_package := $(intermediates.COMMON)/package-export.apk $(my_res_package): PRIVATE_RESOURCE_EXPORT_PACKAGE := $(resource_export_package) $(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(resource_export_package) endif ifdef proguard_options_file $(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(proguard_options_file) endif $(my_res_package): $(full_android_manifest) $(my_static_library_resources) $(my_static_library_transitive_resource_packages_lists) $(my_shared_library_resources) $(my_res_package): $(my_full_asset_paths) $(my_res_package): $(my_res_resources_flat) $(my_overlay_resources_flat) \ $(my_resources_flata) $(my_static_library_resources) $(my_static_library_extra_packages) \ $(AAPT2) $(SOONG_ZIP) $(EXTRACT_JAR_PACKAGES) @echo "AAPT2 link $@" $(call aapt2-link) ifdef R_file_stamp @rm -f $(PRIVATE_R_FILE_STAMP) $(call find-generated-R.java,$(PRIVATE_JAVA_GEN_DIR),$(PRIVATE_R_FILE_STAMP)) endif ifdef LOCAL_EXPORT_PACKAGE_RESOURCES @rm -f $(PRIVATE_RESOURCE_EXPORT_PACKAGE) cp $@ $(PRIVATE_RESOURCE_EXPORT_PACKAGE) endif # Clear inputs only used in this file, so that they're not re-used during the next build my_res_resources := my_overlay_resources := my_compiled_res_base_dir := my_asset_dirs := my_full_asset_paths := my_apk_split_configs := my_generated_res_dirs := my_generated_res_dirs_deps := my_generated_res_zips := ================================================ FILE: core/aapt_flags.mk ================================================ ## AAPT Flags # aapt doesn't accept multiple --extra-packages flags. # We have to collapse them into a single --extra-packages flag here. LOCAL_AAPT_FLAGS := $(strip $(LOCAL_AAPT_FLAGS)) ifdef LOCAL_AAPT_FLAGS ifeq ($(filter 0 1,$(words $(filter --extra-packages,$(LOCAL_AAPT_FLAGS)))),) aapt_flags := $(subst --extra-packages$(space),--extra-packages@,$(LOCAL_AAPT_FLAGS)) aapt_flags_extra_packages := $(patsubst --extra-packages@%,%,$(filter --extra-packages@%,$(aapt_flags))) aapt_flags_extra_packages := $(sort $(subst :,$(space),$(aapt_flags_extra_packages))) LOCAL_AAPT_FLAGS := $(filter-out --extra-packages@%,$(aapt_flags)) \ --extra-packages $(subst $(space),:,$(aapt_flags_extra_packages)) aapt_flags_extra_packages := aapt_flags := endif endif ================================================ FILE: core/allowed_ndk_types.mk ================================================ # Determines the types of NDK modules the current module is allowed to link to. # Input variables: # LOCAL_MODULE # LOCAL_MODULE_CLASS # LOCAL_NDK_STL_VARIANT # LOCAL_SDK_VERSION # Output variables: # my_ndk_stl_family: Family of the NDK STL. # my_ndk_stl_link_type: STL link type, static or shared. # my_allowed_ndk_types: Types of NDK modules that may be linked. # my_warn_ndk_types: Types of NDK modules that shouldn't be linked, but are. my_allowed_ndk_types := my_warn_ndk_types := my_ndk_stl_family := my_ndk_stl_link_type := ifdef LOCAL_SDK_VERSION ifeq ($(LOCAL_NDK_STL_VARIANT),) my_ndk_stl_family := system my_ndk_stl_link_type := shared else ifeq ($(LOCAL_NDK_STL_VARIANT),system) my_ndk_stl_family := system my_ndk_stl_link_type := shared else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared) my_ndk_stl_family := libc++ my_ndk_stl_link_type := shared else ifeq ($(LOCAL_NDK_STL_VARIANT),c++_static) my_ndk_stl_family := libc++ my_ndk_stl_link_type := static else ifeq ($(LOCAL_NDK_STL_VARIANT),none) my_ndk_stl_family := none my_ndk_stl_link_type := none else $(call pretty-error,invalid LOCAL_NDK_STL_VARIANT: $(LOCAL_NDK_STL_VARIANT)) endif ifeq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES) # The "none" link type indicates that nothing is actually linked. Since # this is a static library, it's still up to the final use of the # library whether a static or shared STL should be used. my_ndk_stl_link_type := none endif # The system STL is only the C++ ABI layer, so it's compatible with any STL. my_allowed_ndk_types += native:ndk:system:shared my_allowed_ndk_types += native:ndk:system:none # Libaries that don't use the STL can be linked to anything. my_allowed_ndk_types += native:ndk:none:none # And it's always okay to link a static library that uses your own STL type. # Since nothing was actually linked for the static library, it is up to the # first linked library in the dependency chain which gets used. my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):none ifeq ($(LOCAL_MODULE_CLASS),APPS) # For an app package, it's actually okay to depend on any set of STLs. # If any of the individual libraries depend on each other they've # already been checked for consistency, and if they don't they'll be # kept isolated by RTLD_LOCAL anyway. my_allowed_ndk_types += \ native:ndk:libc++:shared native:ndk:libc++:static # The "none" link type that used by static libraries is intentionally # omitted here. We should only be dealing with shared libraries in # LOCAL_JNI_SHARED_LIBRARIES. else ifeq ($(my_ndk_stl_link_type),shared) # Modules linked to a shared STL can only use another shared STL. my_allowed_ndk_types += native:ndk:$(my_ndk_stl_family):shared endif # Else we are a non-static library that uses a static STL, and are # incompatible with all other shared libraries that use an STL. else my_allowed_ndk_types := \ native:ndk:none:none \ native:ndk:system:none \ native:ndk:system:shared \ ifeq ($(LOCAL_MODULE_CLASS),APPS) # CTS is bad and it should feel bad: http://b/13249737 my_warn_ndk_types += native:ndk:libc++:static endif endif ================================================ FILE: core/android_manifest.mk ================================================ # Handle AndroidManifest.xmls # Input: LOCAL_MANIFEST_FILE, LOCAL_FULL_MANIFEST_FILE, LOCAL_FULL_LIBS_MANIFEST_FILES, # LOCAL_USE_EMBEDDED_NATIVE_LIBS # Output: full_android_manifest ifeq ($(strip $(LOCAL_MANIFEST_FILE)),) LOCAL_MANIFEST_FILE := AndroidManifest.xml endif ifdef LOCAL_FULL_MANIFEST_FILE main_android_manifest := $(LOCAL_FULL_MANIFEST_FILE) else main_android_manifest := $(LOCAL_PATH)/$(LOCAL_MANIFEST_FILE) endif LOCAL_STATIC_JAVA_AAR_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)) my_full_libs_manifest_files := ifndef LOCAL_DONT_MERGE_MANIFESTS my_full_libs_manifest_files += $(LOCAL_FULL_LIBS_MANIFEST_FILES) my_full_libs_manifest_files += $(foreach lib, $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/manifest/AndroidManifest.xml) endif full_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml ifneq (,$(strip $(my_full_libs_manifest_files))) # Set up rules to merge library manifest files fixed_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml.fixed $(full_android_manifest): PRIVATE_LIBS_MANIFESTS := $(my_full_libs_manifest_files) $(full_android_manifest): $(ANDROID_MANIFEST_MERGER) $(full_android_manifest) : $(fixed_android_manifest) $(my_full_libs_manifest_files) @echo "Merge android manifest files: $@ <-- $< $(PRIVATE_LIBS_MANIFESTS)" @mkdir -p $(dir $@) $(hide) $(ANDROID_MANIFEST_MERGER) --main $< \ --libs $(call normalize-path-list,$(PRIVATE_LIBS_MANIFESTS)) \ --out $@ else fixed_android_manifest := $(full_android_manifest) endif my_target_sdk_version := $(call module-target-sdk-version) my_min_sdk_version := $(call module-min-sdk-version) ifdef TARGET_BUILD_APPS ifndef TARGET_BUILD_USE_PREBUILT_SDKS ifeq ($(my_target_sdk_version),$(PLATFORM_VERSION_CODENAME)) ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT my_target_sdk_version := $(my_target_sdk_version).$$(cat $(API_FINGERPRINT)) my_min_sdk_version := $(my_min_sdk_version).$$(cat $(API_FINGERPRINT)) $(fixed_android_manifest): $(API_FINGERPRINT) else ifdef UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA my_target_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA) my_min_sdk_version := $(UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA) endif endif endif endif $(fixed_android_manifest): PRIVATE_MIN_SDK_VERSION := $(my_min_sdk_version) $(fixed_android_manifest): PRIVATE_TARGET_SDK_VERSION := $(my_target_sdk_version) my_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs $(fixed_android_manifest): PRIVATE_EXPORTED_SDK_LIBS_FILE := $(my_exported_sdk_libs_file) $(fixed_android_manifest): $(my_exported_sdk_libs_file) my_manifest_fixer_flags := ifneq ($(LOCAL_MODULE_CLASS),APPS) my_manifest_fixer_flags += --library endif ifeq ($(LOCAL_PRIVATE_PLATFORM_APIS),true) my_manifest_fixer_flags += --uses-non-sdk-api endif ifeq (true,$(LOCAL_USE_EMBEDDED_DEX)) my_manifest_fixer_flags += --use-embedded-dex endif ifeq ($(LOCAL_MODULE_CLASS),APPS) ifeq (true,$(call math_gt_or_eq,$(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23)) ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS)) my_manifest_fixer_flags += --extract-native-libs=false else my_manifest_fixer_flags += --extract-native-libs=true endif else ifeq (true,$(LOCAL_USE_EMBEDDED_NATIVE_LIBS)) $(call pretty-error,LOCAL_USE_EMBEDDED_NATIVE_LIBS is set but minSdkVersion $(call module-min-sdk-version) does not support it) endif endif # TODO: Replace this hardcoded list of optional uses-libraries with build logic # that propagates optionality via the generated exported-sdk-libs files. # Hardcodng doesn't scale and enforces a single choice on each library, while in # reality this is a choice of the library users (which may differ). my_optional_sdk_lib_names := \ android.test.base \ android.test.mock \ androidx.window.extensions \ androidx.window.sidecar $(fixed_android_manifest): PRIVATE_MANIFEST_FIXER_FLAGS := $(my_manifest_fixer_flags) # These two libs are added as optional dependencies ( with # android:required set to false). This is because they haven't existed in pre-P # devices, but classes in them were in bootclasspath jars, etc. So making them # hard dependencies (andriod:required=true) would prevent apps from being # installed to such legacy devices. $(fixed_android_manifest): PRIVATE_OPTIONAL_SDK_LIB_NAMES := $(my_optional_sdk_lib_names) $(fixed_android_manifest): $(MANIFEST_FIXER) $(fixed_android_manifest): $(main_android_manifest) echo $(PRIVATE_OPTIONAL_SDK_LIB_NAMES) | tr ' ' '\n' > $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional @echo "Fix manifest: $@" $(MANIFEST_FIXER) \ --minSdkVersion $(PRIVATE_MIN_SDK_VERSION) \ --targetSdkVersion $(PRIVATE_TARGET_SDK_VERSION) \ --raise-min-sdk-version \ $(PRIVATE_MANIFEST_FIXER_FLAGS) \ $(if (PRIVATE_EXPORTED_SDK_LIBS_FILE),\ $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -v -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\ --uses-library\ /' | tr '\n' ' ') \ $$(cat $(PRIVATE_EXPORTED_SDK_LIBS_FILE) | grep -f $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional | sort -u | sed -e 's/^/\ --optional-uses-library\ /' | tr '\n' ' ') \ ) \ $< $@ rm $(PRIVATE_EXPORTED_SDK_LIBS_FILE).optional my_optional_sdk_lib_names := ================================================ FILE: core/android_soong_config_vars.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This file defines the Soong Config Variable namespace ANDROID, and also any # variables in that namespace. # The expectation is that no vendor should be using the ANDROID namespace. This # check ensures that we don't collide with any existing vendor usage. ifdef SOONG_CONFIG_ANDROID $(error The Soong config namespace ANDROID is reserved.) endif $(call add_soong_config_namespace,ANDROID) # Add variables to the namespace below: $(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE) $(call soong_config_set_bool,ANDROID,BOARD_USES_RECOVERY_AS_BOOT,$(BOARD_USES_RECOVERY_AS_BOOT)) $(call soong_config_set_bool,ANDROID,BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT,$(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT)) $(call add_soong_config_var,ANDROID,CHECK_DEV_TYPE_VIOLATIONS) $(call soong_config_set_bool,ANDROID,HAS_BOARD_SYSTEM_EXT_PREBUILT_DIR,$(if $(BOARD_SYSTEM_EXT_PREBUILT_DIR),true,false)) $(call soong_config_set_bool,ANDROID,HAS_BOARD_PRODUCT_PREBUILT_DIR,$(if $(BOARD_PRODUCT_PREBUILT_DIR),true,false)) $(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_VERSION) $(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_COMPAT_VERSIONS) $(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT) $(call soong_config_set_bool,ANDROID,RELEASE_BOARD_API_LEVEL_FROZEN,$(RELEASE_BOARD_API_LEVEL_FROZEN)) $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_DRMSERVER) $(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64) $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER) $(call soong_config_set_bool,ANDROID,TARGET_SUPPORTS_32_BIT_APPS,$(if $(filter true,$(TARGET_SUPPORTS_32_BIT_APPS)),true,false)) $(call soong_config_set_bool,ANDROID,TARGET_SUPPORTS_64_BIT_APPS,$(if $(filter true,$(TARGET_SUPPORTS_64_BIT_APPS)),true,false)) $(call add_soong_config_var,ANDROID,BOARD_GENFS_LABELS_VERSION) $(call soong_config_set_bool,ANDROID,PRODUCT_FSVERITY_GENERATE_METADATA,$(if $(filter true,$(PRODUCT_FSVERITY_GENERATE_METADATA)),true,false)) $(call add_soong_config_var,ANDROID,ADDITIONAL_M4DEFS,$(if $(BOARD_SEPOLICY_M4DEFS),$(addprefix -D,$(BOARD_SEPOLICY_M4DEFS)))) $(call add_soong_config_var,ANDROID,TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS) # For BUILDING_GSI $(call soong_config_set_bool,gsi,building_gsi,$(if $(filter true,$(BUILDING_GSI)),true,false)) # For bootable/recovery RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 $(call soong_config_set, recovery, recovery_api_version, $(RECOVERY_API_VERSION)) $(call soong_config_set, recovery, recovery_fstab_version, $(RECOVERY_FSTAB_VERSION)) $(call soong_config_set_bool, recovery ,target_userimages_use_f2fs ,$(if $(TARGET_USERIMAGES_USE_F2FS),true,false)) $(call soong_config_set_bool, recovery ,has_board_cacheimage_partition_size ,$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),true,false)) ifdef TARGET_RECOVERY_UI_LIB $(call soong_config_set_string_list, recovery, target_recovery_ui_lib, $(TARGET_RECOVERY_UI_LIB)) endif # For Sanitizers $(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false)) $(call soong_config_set_bool,ANDROID,HWASAN_ENABLED,$(if $(filter hwaddress,$(SANITIZE_TARGET)),true,false)) $(call soong_config_set_bool,ANDROID,SANITIZE_TARGET_SYSTEM_ENABLED,$(if $(filter true,$(SANITIZE_TARGET_SYSTEM)),true,false)) # For init.environ.rc $(call soong_config_set_bool,ANDROID,GCOV_COVERAGE,$(NATIVE_COVERAGE)) $(call soong_config_set_bool,ANDROID,CLANG_COVERAGE,$(CLANG_COVERAGE)) $(call soong_config_set,ANDROID,SCUDO_ALLOCATION_RING_BUFFER_SIZE,$(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)) $(call soong_config_set_bool,ANDROID,EMMA_INSTRUMENT,$(if $(filter true,$(EMMA_INSTRUMENT)),true,false)) # PRODUCT_PRECOMPILED_SEPOLICY defaults to true. Explicitly check if it's "false" or not. $(call soong_config_set_bool,ANDROID,PRODUCT_PRECOMPILED_SEPOLICY,$(if $(filter false,$(PRODUCT_PRECOMPILED_SEPOLICY)),false,true)) # For art modules $(call soong_config_set_bool,art_module,host_prefer_32_bit,$(if $(filter true,$(HOST_PREFER_32_BIT)),true,false)) ifdef ART_DEBUG_OPT_FLAG $(call soong_config_set,art_module,art_debug_opt_flag,$(ART_DEBUG_OPT_FLAG)) endif # The default value of ART_BUILD_HOST_DEBUG is true $(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true)) # For chre $(call soong_config_set_bool,chre,chre_daemon_lpma_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false)) $(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false)) $(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false)) $(call soong_config_set_bool,chre,building_vendor_image,$(if $(filter true,$(BUILDING_VENDOR_IMAGE)),true,false)) $(call soong_config_set_bool,chre,chre_usf_daemon_enabled,$(if $(filter true,$(CHRE_USF_DAEMON_ENABLED)),true,false)) ifdef TARGET_BOARD_AUTO $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO)) endif # Apex build mode variables ifdef APEX_BUILD_FOR_PRE_S_DEVICES $(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static) else ifdef KEEP_APEX_INHERIT $(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static) endif endif # Enable SystemUI optimizations by default unless explicitly set. SYSTEMUI_OPTIMIZE_JAVA ?= true $(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA) # Flag to use baseline profile for SystemUI. $(call soong_config_set,ANDROID,release_systemui_use_speed_profile,$(RELEASE_SYSTEMUI_USE_SPEED_PROFILE)) # Flag for enabling compose for Launcher. $(call soong_config_set,ANDROID,release_enable_compose_in_launcher,$(RELEASE_ENABLE_COMPOSE_IN_LAUNCHER)) ifdef PRODUCT_AVF_ENABLED $(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED)) endif # Enable AVF remote attestation according to the flag value if PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED is not # set to true explicitly. ifneq (true,$(PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED)) $(call add_soong_config_var_value,ANDROID,avf_remote_attestation_enabled,$(RELEASE_AVF_ENABLE_REMOTE_ATTESTATION)) endif ifdef PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION $(call add_soong_config_var_value,ANDROID,avf_microdroid_guest_gki_version,$(PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION)) endif ifdef TARGET_BOOTS_16K $(call soong_config_set_bool,ANDROID,target_boots_16k,$(filter true,$(TARGET_BOOTS_16K))) endif ifdef PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED $(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,$(PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED)) else $(call add_soong_config_var_value,ANDROID,cgroup_v2_sys_app_isolation,true) endif $(call add_soong_config_var_value,ANDROID,release_avf_allow_preinstalled_apps,$(RELEASE_AVF_ALLOW_PREINSTALLED_APPS)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_device_assignment,$(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_dice_changes,$(RELEASE_AVF_ENABLE_DICE_CHANGES)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_early_vm,$(RELEASE_AVF_ENABLE_EARLY_VM)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_llpvm_changes,$(RELEASE_AVF_ENABLE_LLPVM_CHANGES)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_multi_tenant_microdroid_vm,$(RELEASE_AVF_ENABLE_MULTI_TENANT_MICRODROID_VM)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_network,$(RELEASE_AVF_ENABLE_NETWORK)) # TODO(b/341292601): This flag is needed until the V release. We with clean it up after V together # with most of the release_avf_ flags here. $(call add_soong_config_var_value,ANDROID,release_avf_enable_remote_attestation,$(RELEASE_AVF_ENABLE_REMOTE_ATTESTATION)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_vendor_modules,$(RELEASE_AVF_ENABLE_VENDOR_MODULES)) $(call add_soong_config_var_value,ANDROID,release_avf_enable_virt_cpufreq,$(RELEASE_AVF_ENABLE_VIRT_CPUFREQ)) $(call add_soong_config_var_value,ANDROID,release_avf_microdroid_kernel_version,$(RELEASE_AVF_MICRODROID_KERNEL_VERSION)) $(call add_soong_config_var_value,ANDROID,release_avf_support_custom_vm_with_paravirtualized_devices,$(RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES)) $(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI)) $(call add_soong_config_var_value,ANDROID,release_libpower_no_lock_binder_txn,$(RELEASE_LIBPOWER_NO_LOCK_BINDER_TXN)) $(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES)) $(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE)) ifneq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT))) # write appcompat system properties on userdebug and eng builds $(call add_soong_config_var_value,ANDROID,release_write_appcompat_override_system_properties,true) endif # Enable system_server optimizations by default unless explicitly set or if # there may be dependent runtime jars. # TODO(b/240588226): Remove the off-by-default exceptions after handling # system_server jars automatically w/ R8. ifeq (true,$(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS)) # If system_server jar ordering is broken, don't assume services.jar can be # safely optimized in isolation, as there may be dependent jars. SYSTEM_OPTIMIZE_JAVA ?= false else ifneq (platform:services,$(lastword $(PRODUCT_SYSTEM_SERVER_JARS))) # If services is not the final jar in the dependency ordering, don't assume # it can be safely optimized in isolation, as there may be dependent jars. # TODO(b/212737576): Remove this exception after integrating use of `$(system_server_trace_refs)`. SYSTEM_OPTIMIZE_JAVA ?= false else SYSTEM_OPTIMIZE_JAVA ?= true endif ifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA)) SYSTEM_OPTIMIZE_JAVA := true endif $(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA) $(call add_soong_config_var,ANDROID,FULL_SYSTEM_OPTIMIZE_JAVA) ifeq (true, $(SYSTEM_OPTIMIZE_JAVA)) # Create a list of (non-prefixed) system server jars that follow `services` in # the classpath. This can be used when optimizing `services` to trace any # downstream references that need keeping. # Example: "foo:service1 platform:services bar:services2" -> "services2" system_server_jars_dependent_on_services := $(shell \ echo "$(PRODUCT_SYSTEM_SERVER_JARS)" | \ awk '{found=0; for(i=1;i<=NF;i++){if($$i=="platform:services"){found=1; continue} if(found){split($$i,a,":"); print a[2]}}}' | \ xargs) ifneq ($(strip $(system_server_jars_dependent_on_services)),) $(call soong_config_set_string_list,ANDROID,system_server_trace_refs,$(system_server_jars_dependent_on_services)) endif endif # TODO(b/319697968): Remove this build flag support when metalava fully supports flagged api $(call soong_config_set,ANDROID,release_hidden_api_exportable_stubs,$(RELEASE_HIDDEN_API_EXPORTABLE_STUBS)) # Check for SupplementalApi module. ifeq ($(wildcard packages/modules/SupplementalApi),) $(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,false) else $(call add_soong_config_var_value,ANDROID,include_nonpublic_framework_api,true) endif # Add nfc build flag to soong ifneq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci) $(call soong_config_set,bootclasspath,nfc_apex_bootclasspath_fragment,true) endif # Add uwb build flag to soong $(call soong_config_set,bootclasspath,release_ranging_stack,$(RELEASE_RANGING_STACK)) # Add crashrecovery build flag to soong $(call soong_config_set,ANDROID,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE)) # Add crashrecovery file move flags to soong, for both platform and module ifeq (true,$(RELEASE_CRASHRECOVERY_FILE_MOVE)) $(call soong_config_set,ANDROID,crashrecovery_files_in_module,true) $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,false) else $(call soong_config_set,ANDROID,crashrecovery_files_in_module,false) $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,true) endif # Required as platform_bootclasspath is using this namespace $(call soong_config_set,bootclasspath,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE)) # Add ondeviceintelligence module build flag to soong ifeq (true,$(RELEASE_ONDEVICE_INTELLIGENCE_MODULE)) $(call soong_config_set,ANDROID,release_ondevice_intelligence_module,true) # Required as platform_bootclasspath is using this namespace $(call soong_config_set,bootclasspath,release_ondevice_intelligence_module,true) else $(call soong_config_set,ANDROID,release_ondevice_intelligence_platform,true) $(call soong_config_set,bootclasspath,release_ondevice_intelligence_platform,true) endif # Add uprobestats build flag to soong $(call soong_config_set,ANDROID,release_uprobestats_module,$(RELEASE_UPROBESTATS_MODULE)) # Add uprobestats file move flags to soong, for both platform and module ifeq (true,$(RELEASE_UPROBESTATS_FILE_MOVE)) $(call soong_config_set,ANDROID,uprobestats_files_in_module,true) $(call soong_config_set,ANDROID,uprobestats_files_in_platform,false) else $(call soong_config_set,ANDROID,uprobestats_files_in_module,false) $(call soong_config_set,ANDROID,uprobestats_files_in_platform,true) endif # Enable Profiling module. Also used by platform_bootclasspath. $(call soong_config_set,ANDROID,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE)) $(call soong_config_set,bootclasspath,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE)) # Move VCN from platform to the Tethering module; used by both platform and module $(call soong_config_set,ANDROID,is_vcn_in_mainline,$(RELEASE_MOVE_VCN_TO_MAINLINE)) # Add perf-setup build flag to soong # Note: BOARD_PERFSETUP_SCRIPT location must be under platform_testing/scripts/perf-setup/. ifdef BOARD_PERFSETUP_SCRIPT $(call soong_config_set,perf,board_perfsetup_script,$(notdir $(BOARD_PERFSETUP_SCRIPT))) endif # Add target_use_pan_display flag for hardware/libhardware:gralloc.default $(call soong_config_set_bool,gralloc,target_use_pan_display,$(if $(filter true,$(TARGET_USE_PAN_DISPLAY)),true,false)) # Add use_camera_v4l2_hal flag for hardware/libhardware/modules/camera/3_4:camera.v4l2 $(call soong_config_set_bool,camera,use_camera_v4l2_hal,$(if $(filter true,$(USE_CAMERA_V4L2_HAL)),true,false)) # Add audioserver_multilib flag for hardware/interfaces/soundtrigger/2.0/default:android.hardware.soundtrigger@2.0-impl ifneq ($(strip $(AUDIOSERVER_MULTILIB)),) $(call soong_config_set,soundtrigger,audioserver_multilib,$(AUDIOSERVER_MULTILIB)) endif # Add sim_count, disable_rild_oem_hook, and use_aosp_rild flag for ril related modules $(call soong_config_set,ril,sim_count,$(SIM_COUNT)) ifneq ($(DISABLE_RILD_OEM_HOOK), false) $(call soong_config_set_bool,ril,disable_rild_oem_hook,true) endif ifneq ($(ENABLE_VENDOR_RIL_SERVICE), true) $(call soong_config_set_bool,ril,use_aosp_rild,true) endif # Export target_board_platform to soong for hardware/google/graphics/common/libmemtrack:memtrack.$(TARGET_BOARD_PLATFORM) $(call soong_config_set,ANDROID,target_board_platform,$(TARGET_BOARD_PLATFORM)) # Export board_uses_scaler_m2m1shot and board_uses_align_restriction to soong for hardware/google/graphics/common/libscaler:libexynosscaler $(call soong_config_set_bool,google_graphics,board_uses_scaler_m2m1shot,$(if $(filter true,$(BOARD_USES_SCALER_M2M1SHOT)),true,false)) $(call soong_config_set_bool,google_graphics,board_uses_align_restriction,$(if $(filter true,$(BOARD_USES_ALIGN_RESTRICTION)),true,false)) # Export related variables to soong for hardware/google/graphics/common/libacryl:libacryl ifdef BOARD_LIBACRYL_DEFAULT_COMPOSITOR $(call soong_config_set,acryl,libacryl_default_compositor,$(BOARD_LIBACRYL_DEFAULT_COMPOSITOR)) endif ifdef BOARD_LIBACRYL_DEFAULT_SCALER $(call soong_config_set,acryl,libacryl_default_scaler,$(BOARD_LIBACRYL_DEFAULT_SCALER)) endif ifdef BOARD_LIBACRYL_DEFAULT_BLTER $(call soong_config_set,acryl,libacryl_default_blter,$(BOARD_LIBACRYL_DEFAULT_BLTER)) endif ifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN #BOARD_LIBACRYL_G2D_HDR_PLUGIN is set in each board config $(call soong_config_set_bool,acryl,libacryl_use_g2d_hdr_plugin,true) endif # Export related variables to soong for hardware/google/graphics/common/BoardConfigCFlags.mk $(call soong_config_set_bool,google_graphics,hwc_no_support_skip_validate,$(if $(filter true,$(HWC_NO_SUPPORT_SKIP_VALIDATE)),true,false)) $(call soong_config_set_bool,google_graphics,hwc_support_color_transform,$(if $(filter true,$(HWC_SUPPORT_COLOR_TRANSFORM)),true,false)) $(call soong_config_set_bool,google_graphics,hwc_support_render_intent,$(if $(filter true,$(HWC_SUPPORT_RENDER_INTENT)),true,false)) $(call soong_config_set_bool,google_graphics,board_uses_virtual_display,$(if $(filter true,$(BOARD_USES_VIRTUAL_DISPLAY)),true,false)) $(call soong_config_set_bool,google_graphics,board_uses_dt,$(if $(filter true,$(BOARD_USES_DT)),true,false)) $(call soong_config_set_bool,google_graphics,board_uses_decon_64bit_address,$(if $(filter true,$(BOARD_USES_DECON_64BIT_ADDRESS)),true,false)) $(call soong_config_set_bool,google_graphics,board_uses_hdrui_gles_conversion,$(if $(filter true,$(BOARD_USES_HDRUI_GLES_CONVERSION)),true,false)) $(call soong_config_set_bool,google_graphics,uses_idisplay_intf_sec,$(if $(filter true,$(USES_IDISPLAY_INTF_SEC)),true,false)) # Variables for fs_config $(call soong_config_set_bool,fs_config,vendor,$(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),true,false)) $(call soong_config_set_bool,fs_config,oem,$(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),true,false)) $(call soong_config_set_bool,fs_config,odm,$(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),true,false)) $(call soong_config_set_bool,fs_config,vendor_dlkm,$(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) $(call soong_config_set_bool,fs_config,odm_dlkm,$(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) $(call soong_config_set_bool,fs_config,system_dlkm,$(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) # Variables for telephony $(call soong_config_set_bool,telephony,sec_cp_secure_boot,$(if $(filter true,$(SEC_CP_SECURE_BOOT)),true,false)) $(call soong_config_set_bool,telephony,cbd_protocol_sit,$(if $(filter true,$(CBD_PROTOCOL_SIT)),true,false)) $(call soong_config_set_bool,telephony,use_radioexternal_hal_aidl,$(if $(filter true,$(USE_RADIOEXTERNAL_HAL_AIDL)),true,false)) # Variables for hwcomposer.$(TARGET_BOARD_PLATFORM) $(call soong_config_set_bool,google_graphics,board_uses_hwc_services,$(if $(filter true,$(BOARD_USES_HWC_SERVICES)),true,false)) # Variables for controlling android.hardware.composer.hwc3-service.pixel $(call soong_config_set,google_graphics,board_hwc_version,$(BOARD_HWC_VERSION)) # Flag ExcludeExtractApk is to support "extract_apk" property for the following conditions. ifneq ($(WITH_DEXPREOPT),true) $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true) endif ifeq ($(DONT_DEXPREOPT_PREBUILTS),true) $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true) endif ifeq ($(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY),true) $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true) endif # Variables for extra branches # TODO(b/383238397): Use bootstrap_go_package to enable extra flags. -include vendor/google/build/extra_soong_config_vars.mk # Variable for CI test packages ifneq ($(filter arm x86 true,$(TARGET_ARCH) $(TARGET_2ND_ARCH) $(TARGET_ENABLE_MEDIADRM_64)),) $(call soong_config_set_bool,ci_tests,uses_widevine_tests, true) endif # Flags used in GTVS prebuilt apps $(call soong_config_set_bool,GTVS,GTVS_COMPRESSED_PREBUILTS,$(if $(findstring $(GTVS_COMPRESSED_PREBUILTS),true yes),true,false)) $(call soong_config_set_bool,GTVS,GTVS_GMSCORE_BETA,$(if $(findstring $(GTVS_GMSCORE_BETA),true yes),true,false)) $(call soong_config_set_bool,GTVS,GTVS_SETUPWRAITH_BETA,$(if $(findstring $(GTVS_SETUPWRAITH_BETA),true yes),true,false)) $(call soong_config_set_bool,GTVS,PRODUCT_USE_PREBUILT_GTVS,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS),true yes),true,false)) # Flags used in GTVS_GTV prebuilt apps $(call soong_config_set_bool,GTVS_GTV,PRODUCT_USE_PREBUILT_GTVS_GTV,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS_GTV),true yes),true,false)) # Check modules to be built in "otatools-package". ifneq ($(wildcard vendor/google/tools/build_mixed_kernels_ramdisk),) $(call soong_config_set_bool,otatools,use_build_mixed_kernels_ramdisk,true) endif ifneq ($(wildcard bootable/deprecated-ota/applypatch),) $(call soong_config_set_bool,otatools,use_bootable_deprecated_ota_applypatch,true) endif ================================================ FILE: core/app_certificate_validate.mk ================================================ ifeq (true,$(non_system_module)) ifneq (,$(filter $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))%,$(LOCAL_CERTIFICATE))) CERTIFICATE_VIOLATION_MODULES += $(LOCAL_MODULE) ifeq (true,$(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)) $(if $(filter $(LOCAL_MODULE),$(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)),,\ $(call pretty-error,The module in product partition cannot be signed with certificate in system.)) endif endif endif ================================================ FILE: core/app_prebuilt_internal.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################ # Internal build rules for APPS prebuilt modules ############################################################ ifneq (APPS,$(LOCAL_MODULE_CLASS)) $(call pretty-error,app_prebuilt_internal.mk is for APPS modules only) endif ifdef LOCAL_COMPRESSED_MODULE ifneq (true,$(LOCAL_COMPRESSED_MODULE)) $(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE)) endif LOCAL_BUILT_MODULE_STEM := package.apk.gz ifndef LOCAL_INSTALLED_MODULE_STEM PACKAGES.$(LOCAL_MODULE).COMPRESSED := gz LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz endif else # LOCAL_COMPRESSED_MODULE LOCAL_BUILT_MODULE_STEM := package.apk ifndef LOCAL_INSTALLED_MODULE_STEM LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk endif endif # LOCAL_COMPRESSED_MODULE include $(BUILD_SYSTEM)/base_rules.mk built_module := $(LOCAL_BUILT_MODULE) # Run veridex on product, system_ext and vendor modules. # We skip it for unbundled app builds where we cannot build veridex. module_run_appcompat := ifeq (true,$(non_system_module)) ifeq (,$(TARGET_BUILD_APPS)) # ! unbundled app build ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) module_run_appcompat := true endif endif endif PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) my_extract_apk := $(strip $(LOCAL_EXTRACT_APK)) # Select dpi-specific source ifdef LOCAL_DPI_VARIANTS my_dpi := $(firstword $(filter $(LOCAL_DPI_VARIANTS),$(PRODUCT_AAPT_PREF_CONFIG) $(PRODUCT_AAPT_PREBUILT_DPI))) ifdef my_dpi ifdef LOCAL_DPI_FILE_STEM my_prebuilt_dpi_file_stem := $(LOCAL_DPI_FILE_STEM) else my_prebuilt_dpi_file_stem := $(LOCAL_MODULE)_%.apk endif my_prebuilt_src_file := $(dir $(my_prebuilt_src_file))$(subst %,$(my_dpi),$(my_prebuilt_dpi_file_stem)) ifneq ($(strip $(LOCAL_EXTRACT_DPI_APK)),) my_extract_apk := $(subst %,$(my_dpi),$(LOCAL_EXTRACT_DPI_APK)) endif # LOCAL_EXTRACT_DPI_APK endif # my_dpi endif # LOCAL_DPI_VARIANTS ifdef my_extract_apk my_extracted_apk := $(intermediates)/extracted.apk $(my_extracted_apk): PRIVATE_EXTRACT := $(my_extract_apk) $(my_extracted_apk): $(my_prebuilt_src_file) @echo Extract APK: $@ $(hide) mkdir -p $(dir $@) && rm -f $@ $(hide) unzip -p $< $(PRIVATE_EXTRACT) >$@ my_prebuilt_src_file := $(my_extracted_apk) my_extracted_apk := my_extract_apk := endif rs_compatibility_jni_libs := include $(BUILD_SYSTEM)/install_jni_libs.mk ifeq ($(LOCAL_CERTIFICATE),EXTERNAL) # The magic string "EXTERNAL" means this package will be signed with # the default dev key throughout the build process, but we expect # the final package to be signed with a different key. # # This can be used for packages where we don't have access to the # keys, but want the package to be predexopt'ed. LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) PACKAGES.$(LOCAL_MODULE).EXTERNAL_KEY := 1 $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem endif ifeq ($(LOCAL_CERTIFICATE),) # It is now a build error to add a prebuilt .apk without # specifying a key for it. $(error No LOCAL_CERTIFICATE specified for prebuilt "$(my_prebuilt_src_file)") else ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) # The magic string "PRESIGNED" means this package is already checked # signed with its release key. # # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be # mentioned in apkcerts.txt (with certificate set to "PRESIGNED") # but the dexpreopt process will not try to re-sign the app. PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED PACKAGES := $(PACKAGES) $(LOCAL_MODULE) else # If this is not an absolute certificate, assign it to a generic one. ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./) LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE) endif # NOTE(ruperts): Consider moving the logic below out of a conditional, # to avoid the possibility of silently ignoring user settings. PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem PACKAGES := $(PACKAGES) $(LOCAL_MODULE) $(built_module) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem $(built_module) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem additional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8) $(built_module): $(additional_certificates) $(built_module): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates) $(built_module): $(LOCAL_CERTIFICATE_LINEAGE) $(built_module): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE) $(built_module): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION) endif ifneq ($(LOCAL_MODULE_STEM),) PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) else PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) endif include $(BUILD_SYSTEM)/app_certificate_validate.mk # Set a actual_partition_tag (calculated in base_rules.mk) for the package. PACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag) # Disable dex-preopt of prebuilts to save space, if requested. ifndef LOCAL_DEX_PREOPT ifeq ($(DONT_DEXPREOPT_PREBUILTS),true) LOCAL_DEX_PREOPT := false endif endif # If the module is a compressed module, we don't pre-opt it because its final # installation location will be the data partition. ifdef LOCAL_COMPRESSED_MODULE LOCAL_DEX_PREOPT := false endif my_dex_jar := $(my_prebuilt_src_file) dex_preopt_profile_src_file := $(my_prebuilt_src_file) ####################################### # defines built_odex along with rule to install odex my_manifest_or_apk := $(my_prebuilt_src_file) include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk my_manifest_or_apk := ####################################### ifneq ($(LOCAL_REPLACE_PREBUILT_APK_INSTALLED),) # There is a replacement for the prebuilt .apk we can install without any processing. $(built_module) : $(LOCAL_REPLACE_PREBUILT_APK_INSTALLED) $(transform-prebuilt-to-target) else # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED # If the SDK version is 30 or higher, the apk is signed with a v2+ scheme. # Altering it will invalidate the signature. Just do error checks instead. do_not_alter_apk := ifeq (PRESIGNED,$(LOCAL_CERTIFICATE)) ifneq (,$(LOCAL_SDK_VERSION)) ifeq ($(call math_is_number,$(LOCAL_SDK_VERSION)),true) ifeq ($(call math_gt,$(LOCAL_SDK_VERSION),29),true) do_not_alter_apk := true endif endif # TODO: Add system_current after fixing the existing modules. ifneq ($(filter current test_current core_current,$(LOCAL_SDK_VERSION)),) do_not_alter_apk := true endif endif endif ifeq ($(do_not_alter_apk),true) $(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN) $(transform-prebuilt-to-target) $(check-jni-dex-compression) $(check-package-alignment) else # Sign and align non-presigned .apks. # The embedded prebuilt jni to uncompress. ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) # For PRESIGNED apks we must uncompress every .so file: # even if the .so file isn't for the current TARGET_ARCH, # we can't strip the file. embedded_prebuilt_jni_libs := endif ifndef embedded_prebuilt_jni_libs # No LOCAL_PREBUILT_JNI_LIBS, uncompress all. embedded_prebuilt_jni_libs := endif $(built_module): PRIVATE_EMBEDDED_JNI_LIBS := $(embedded_prebuilt_jni_libs) ifdef LOCAL_COMPRESSED_MODULE $(built_module) : $(GZIP) endif ifeq ($(module_run_appcompat),true) $(built_module) : $(appcompat-files) $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) endif ifeq ($(module_run_appcompat),true) $(built_module) : $(AAPT2) endif $(built_module) : $(my_prebuilt_src_file) | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) $(transform-prebuilt-to-target) $(uncompress-prebuilt-embedded-jni-libs) $(remove-unwanted-prebuilt-embedded-jni-libs) ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) $(uncompress-dexs) endif # LOCAL_UNCOMPRESS_DEX ifneq ($(LOCAL_CERTIFICATE),PRESIGNED) ifeq ($(module_run_appcompat),true) $(call appcompat-header, aapt2) $(run-appcompat) endif # module_run_appcompat $(sign-package) # No need for align-package because sign-package takes care of alignment else # LOCAL_CERTIFICATE == PRESIGNED $(align-package) endif # LOCAL_CERTIFICATE ifdef LOCAL_COMPRESSED_MODULE $(compress-package) endif # LOCAL_COMPRESSED_MODULE endif # ! do_not_alter_apk endif # ! LOCAL_REPLACE_PREBUILT_APK_INSTALLED ############################### ## Install split apks. ifdef LOCAL_PACKAGE_SPLITS ifdef LOCAL_COMPRESSED_MODULE $(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs) endif # LOCAL_COMPRESSED_MODULE # LOCAL_PACKAGE_SPLITS is a list of apks to be installed. built_apk_splits := $(addprefix $(intermediates)/,$(notdir $(LOCAL_PACKAGE_SPLITS))) installed_apk_splits := $(addprefix $(my_module_path)/,$(notdir $(LOCAL_PACKAGE_SPLITS))) # Rules to sign the split apks. my_src_dir := $(sort $(dir $(LOCAL_PACKAGE_SPLITS))) ifneq (1,$(words $(my_src_dir))) $(error You must put all the split source apks in the same folder: $(LOCAL_PACKAGE_SPLITS)) endif my_src_dir := $(LOCAL_PATH)/$(my_src_dir) $(built_apk_splits) : $(LOCAL_CERTIFICATE).pk8 $(LOCAL_CERTIFICATE).x509.pem | $(ZIPALIGN) $(ZIP2ZIP) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) $(built_apk_splits) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8 $(built_apk_splits) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem $(built_apk_splits) : $(intermediates)/%.apk : $(my_src_dir)/%.apk $(copy-file-to-new-target) $(sign-package) # Rules to install the split apks. $(installed_apk_splits) : $(my_module_path)/%.apk : $(intermediates)/%.apk @echo "Install: $@" $(copy-file-to-new-target) # Register the additional built and installed files. ALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits) ALL_MODULES.$(my_register_name).BUILT_INSTALLED += \ $(foreach s,$(LOCAL_PACKAGE_SPLITS),$(intermediates)/$(notdir $(s)):$(my_module_path)/$(notdir $(s))) # Make sure to install the splits when you run "make ". $(my_all_targets): $(installed_apk_splits) endif # LOCAL_PACKAGE_SPLITS ########################################################### ## SBOM generation ########################################################### include $(BUILD_SBOM_GEN) ================================================ FILE: core/art_config.mk ================================================ # ART configuration that has to be determined after product config is resolved. # # Inputs: # PRODUCT_ENABLE_UFFD_GC: See comments in build/make/core/product.mk. # OVERRIDE_ENABLE_UFFD_GC: Overrides PRODUCT_ENABLE_UFFD_GC. Can be passed from the commandline for # debugging purposes. # BOARD_API_LEVEL: See comments in build/make/core/main.mk. # BOARD_SHIPPING_API_LEVEL: See comments in build/make/core/main.mk. # PRODUCT_SHIPPING_API_LEVEL: See comments in build/make/core/product.mk. # # Outputs: # ENABLE_UFFD_GC: Whether to use userfaultfd GC. config_enable_uffd_gc := \ $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default) ifeq (,$(filter default true false,$(config_enable_uffd_gc))) $(error Unknown PRODUCT_ENABLE_UFFD_GC value: $(config_enable_uffd_gc)) endif ENABLE_UFFD_GC := $(config_enable_uffd_gc) # Create APEX_BOOT_JARS_EXCLUDED which is a list of jars to be removed from # ApexBoorJars when built from mainline prebuilts. # Note that RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST is the list of module names # and library names of jars that need to be removed. We have to keep separated list per # release config due to possibility of different prebuilt content. # # If a device has opted to not use google prebuilts (determined using # PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS), then no jars need to be removed. # Example of products where PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS is true are # 1. aosp devices (they do not use google apexes) # 2. hwasan devices (apex prebuilts are not compatible with these devices) # 3. coverage builds ifneq (true, $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS)) APEX_BOOT_JARS_EXCLUDED += $(RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST) endif ================================================ FILE: core/artifact_path_requirements.mk ================================================ # This file contains logic to enforce artifact path requirements # defined in product makefiles. # Fakes don't get installed, and NDK stubs aren't installed to device. static_allowed_patterns := $(TARGET_OUT_FAKE)/% $(SOONG_OUT_DIR)/ndk/% # RROs become REQUIRED by the source module, but are always placed on the vendor partition. static_allowed_patterns += %__auto_generated_characteristics_rro.apk static_allowed_patterns += %__auto_generated_rro_product.apk static_allowed_patterns += %__auto_generated_rro_vendor.apk # Auto-included targets are not considered static_allowed_patterns += $(call product-installed-files,) # $(PRODUCT_OUT)/apex is where shared libraries in APEXes get installed. # The path can be considered as a fake path, as the shared libraries # are installed there just to have symbols files for them under # $(PRODUCT_OUT)/symbols/apex for debugging purpose. The /apex directory # is never compiled into a filesystem image. static_allowed_patterns += $(PRODUCT_OUT)/apex/% ifeq (true,$(BOARD_USES_SYSTEM_OTHER_ODEX)) # Allow system_other odex space optimization. static_allowed_patterns += \ $(TARGET_OUT_SYSTEM_OTHER)/%.odex \ $(TARGET_OUT_SYSTEM_OTHER)/%.vdex \ $(TARGET_OUT_SYSTEM_OTHER)/%.art endif ifneq (,$(filter-out true false relaxed strict,$(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS))$(filter-out 1 0,$(words $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)))) $(error PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS must be one of [true, false, relaxed, strict], found: $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) endif all_offending_files := $(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\ $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \ $(eval ### Verify that the product only produces files inside its path requirements.) \ $(eval allowed := $(PRODUCTS.$(makefile).ARTIFACT_PATH_ALLOWED_LIST)) \ $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \ $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \ $(eval files := $(call product-installed-files, $(makefile))) \ $(eval offending_files := $(filter-out $(path_patterns) $(allowed_patterns) $(static_allowed_patterns),$(files))) \ $(call maybe-print-list-and-error,$(offending_files),\ $(makefile) produces files outside its artifact path requirement. \ Allowed paths are $(subst $(space),$(comma)$(space),$(addsuffix *,$(requirements)))) \ $(eval unused_allowed := $(filter-out $(files),$(allowed_patterns))) \ $(if $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED),, \ $(call maybe-print-list-and-error,$(unused_allowed),$(makefile) includes redundant allowed entries in its artifact path requirement.) \ ) \ $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \ $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_target_FILES))) \ $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \ $(eval all_offending_files += $(files_in_requirement)) \ $(eval allowed := $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST)) \ $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \ $(eval offending_files := $(filter-out $(allowed_patterns),$(files_in_requirement))) \ $(eval enforcement := $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) \ $(if $(filter-out false,$(enforcement)),\ $(call maybe-print-list-and-error,$(offending_files),\ $(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement. \ $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT)) \ $(eval unused_allowed := $(if $(filter true strict,$(enforcement)),\ $(foreach p,$(allowed_patterns),$(if $(filter $(p),$(extra_files)),,$(p))))) \ $(call maybe-print-list-and-error,$(unused_allowed),$(INTERNAL_PRODUCT) includes redundant artifact path requirement allowed list entries.) \ ) \ ) $(PRODUCT_OUT)/offending_artifacts.txt: rm -f $@ $(foreach f,$(sort $(all_offending_files)),echo $(f) >> $@;) ================================================ FILE: core/autogen_test_config.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This build rule allows TradeFed test config file to be created based on # following inputs: # is_native: If the test is a native test. # full_android_manifest: Name of the AndroidManifest file for the test. # Output: # autogen_test_config_file: Path to the test config file generated. autogen_test_config_file := $(dir $(LOCAL_BUILT_MODULE))$(LOCAL_MODULE).config # TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base. autogen_test_install_base := /data/local/tmp # Automatically setup test root for native test. ifeq (true,$(is_native)) ifeq (true,$(LOCAL_VENDOR_MODULE)) autogen_test_install_base = /data/local/tests/vendor endif ifeq (true,$(call module-in-vendor-or-product)) autogen_test_install_base = /data/local/tests/vendor endif endif ifeq (true,$(is_native)) ifeq ($(LOCAL_NATIVE_BENCHMARK),true) autogen_test_config_template := $(NATIVE_BENCHMARK_TEST_CONFIG_TEMPLATE) else ifeq ($(LOCAL_IS_HOST_MODULE),true) autogen_test_config_template := $(NATIVE_HOST_TEST_CONFIG_TEMPLATE) else autogen_test_config_template := $(NATIVE_TEST_CONFIG_TEMPLATE) endif endif # Auto generating test config file for native test $(autogen_test_config_file): PRIVATE_TEST_INSTALL_BASE := $(autogen_test_install_base) $(autogen_test_config_file): PRIVATE_MODULE_NAME := $(LOCAL_MODULE) $(autogen_test_config_file) : $(autogen_test_config_template) @echo "Auto generating test config $(notdir $@)" $(hide) sed 's&{MODULE}&$(PRIVATE_MODULE_NAME)&g;s&{TEST_INSTALL_BASE}&$(PRIVATE_TEST_INSTALL_BASE)&g;s&{EXTRA_CONFIGS}&&g' $< > $@ my_auto_generate_config := true else # Auto generating test config file for instrumentation test ifneq (,$(full_android_manifest)) $(autogen_test_config_file): PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT := $(AUTOGEN_TEST_CONFIG_SCRIPT) $(autogen_test_config_file): PRIVATE_TEST_CONFIG_ANDROID_MANIFEST := $(full_android_manifest) $(autogen_test_config_file): PRIVATE_EMPTY_TEST_CONFIG := $(EMPTY_TEST_CONFIG) $(autogen_test_config_file): PRIVATE_TEMPLATE := $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE) $(autogen_test_config_file) : $(full_android_manifest) $(EMPTY_TEST_CONFIG) $(INSTRUMENTATION_TEST_CONFIG_TEMPLATE) $(AUTOGEN_TEST_CONFIG_SCRIPT) @echo "Auto generating test config $(notdir $@)" @rm -f $@ $(hide) $(PRIVATE_AUTOGEN_TEST_CONFIG_SCRIPT) $@ $(PRIVATE_TEST_CONFIG_ANDROID_MANIFEST) $(PRIVATE_EMPTY_TEST_CONFIG) $(PRIVATE_TEMPLATE) my_auto_generate_config := true endif # ifneq (,$(full_android_manifest)) endif # ifneq (true,$(is_native)) ifeq (true,$(my_auto_generate_config)) LOCAL_INTERMEDIATE_TARGETS += $(autogen_test_config_file) $(LOCAL_BUILT_MODULE): $(autogen_test_config_file) ALL_MODULES.$(my_register_name).auto_test_config := true $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen := true else autogen_test_config_file := endif my_auto_generate_config := ================================================ FILE: core/base_rules.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Catch users that directly include base_rules.mk $(call record-module-type,base_rules) # Users can define base-rules-hook in their buildspec.mk to perform # arbitrary operations as each module is included. ifdef base-rules-hook ifndef _has_warned_about_base_rules_hook $(warning base-rules-hook is deprecated, please remove usages of it and/or convert to Soong.) _has_warned_about_base_rules_hook := true endif $(if $(base-rules-hook),) endif ########################################################### ## Common instructions for a generic module. ########################################################### LOCAL_MODULE := $(strip $(LOCAL_MODULE)) ifeq ($(LOCAL_MODULE),) $(error $(LOCAL_PATH): LOCAL_MODULE is not defined) endif $(call verify-module-name) my_test_data := my_test_config := LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE)) ifdef LOCAL_IS_HOST_MODULE ifneq ($(LOCAL_IS_HOST_MODULE),true) $(error $(LOCAL_PATH): LOCAL_IS_HOST_MODULE must be "true" or empty, not "$(LOCAL_IS_HOST_MODULE)") endif ifeq ($(LOCAL_HOST_PREFIX),) my_prefix := HOST_ else my_prefix := $(LOCAL_HOST_PREFIX) endif my_host := host- my_kind := HOST else my_prefix := TARGET_ my_kind := my_host := endif ifeq ($(my_prefix),HOST_CROSS_) my_host_cross := true else my_host_cross := endif ifeq (true, $(LOCAL_PRODUCT_MODULE)) ifneq (,$(filter $(LOCAL_MODULE),$(PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION))) LOCAL_PRODUCT_MODULE := endif endif _path := $(LOCAL_MODULE_PATH) $(LOCAL_MODULE_PATH_32) $(LOCAL_MODULE_PATH_64) ifneq ($(filter $(TARGET_OUT_VENDOR)%,$(_path)),) LOCAL_VENDOR_MODULE := true else ifneq ($(filter $(TARGET_OUT_OEM)/%,$(_path)),) LOCAL_OEM_MODULE := true else ifneq ($(filter $(TARGET_OUT_ODM)/%,$(_path)),) LOCAL_ODM_MODULE := true else ifneq ($(filter $(TARGET_OUT_PRODUCT)/%,$(_path)),) LOCAL_PRODUCT_MODULE := true else ifneq ($(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(_path)),) LOCAL_SYSTEM_EXT_MODULE := true endif _path := # TODO(b/135957588) Remove following workaround # LOCAL_PRODUCT_SERVICES_MODULE to LOCAL_PRODUCT_MODULE for all Android.mk ifndef LOCAL_PRODUCT_MODULE LOCAL_PRODUCT_MODULE := $(LOCAL_PRODUCT_SERVICES_MODULE) endif ifndef LOCAL_PROPRIETARY_MODULE LOCAL_PROPRIETARY_MODULE := $(LOCAL_VENDOR_MODULE) endif ifndef LOCAL_VENDOR_MODULE LOCAL_VENDOR_MODULE := $(LOCAL_PROPRIETARY_MODULE) endif ifneq ($(filter-out $(LOCAL_PROPRIETARY_MODULE),$(LOCAL_VENDOR_MODULE))$(filter-out $(LOCAL_VENDOR_MODULE),$(LOCAL_PROPRIETARY_MODULE)),) $(call pretty-error,Only one of LOCAL_PROPRIETARY_MODULE[$(LOCAL_PROPRIETARY_MODULE)] and LOCAL_VENDOR_MODULE[$(LOCAL_VENDOR_MODULE)] may be set, or they must be equal) endif ifeq ($(LOCAL_HOST_MODULE),true) my_image_variant := host else ifeq ($(LOCAL_VENDOR_MODULE),true) my_image_variant := vendor else ifeq ($(LOCAL_OEM_MODULE),true) my_image_variant := vendor else ifeq ($(LOCAL_ODM_MODULE),true) my_image_variant := vendor else ifeq ($(LOCAL_PRODUCT_MODULE),true) my_image_variant := product else my_image_variant := core endif non_system_module := $(filter true, \ $(LOCAL_PRODUCT_MODULE) \ $(LOCAL_SYSTEM_EXT_MODULE) \ $(LOCAL_VENDOR_MODULE) \ $(LOCAL_PROPRIETARY_MODULE)) include $(BUILD_SYSTEM)/local_vendor_product.mk # local_current_sdk needs to run before local_systemsdk because the former may override # LOCAL_SDK_VERSION which is used by the latter. include $(BUILD_SYSTEM)/local_current_sdk.mk # Check if the use of System SDK is correct. Note that, for Soong modules, the system sdk version # check is done in Soong. No need to do it twice. ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) include $(BUILD_SYSTEM)/local_systemsdk.mk endif # Ninja has an implicit dependency on the command being run, and kati will # regenerate the ninja manifest if any read makefile changes, so there is no # need to have dependencies on makefiles. # This won't catch all the cases where LOCAL_ADDITIONAL_DEPENDENCIES contains # a .mk file, because a few users of LOCAL_ADDITIONAL_DEPENDENCIES don't include # base_rules.mk, but it will fix the most common ones. LOCAL_ADDITIONAL_DEPENDENCIES := $(filter-out %.mk,$(LOCAL_ADDITIONAL_DEPENDENCIES)) my_bad_deps := $(strip $(foreach dep,$(filter-out | ||,$(LOCAL_ADDITIONAL_DEPENDENCIES)),\ $(if $(findstring /,$(dep)),,$(dep)))) ifneq ($(my_bad_deps),) $(call pretty-warning,"Bad LOCAL_ADDITIONAL_DEPENDENCIES: $(my_bad_deps)") $(call pretty-error,"LOCAL_ADDITIONAL_DEPENDENCIES must only contain paths (not module names)") endif ########################################################### ## Validate and define fallbacks for input LOCAL_* variables. ########################################################### LOCAL_UNINSTALLABLE_MODULE := $(strip $(LOCAL_UNINSTALLABLE_MODULE)) # Only the tags mentioned in this test are expected to be set by module # makefiles. Anything else is either a typo or a source of unexpected # behaviors. ifneq ($(filter-out tests optional samples,$(LOCAL_MODULE_TAGS)),) $(call pretty-error,unusual tags: $(filter-out tests optional samples,$(LOCAL_MODULE_TAGS))) endif LOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS)) ifneq ($(words $(LOCAL_MODULE_CLASS)),1) $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must contain exactly one word, not "$(LOCAL_MODULE_CLASS)") endif my_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32) ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) my_multilib_module_path := $(strip $(LOCAL_MODULE_PATH_$(my_32_64_bit_suffix))) ifdef my_multilib_module_path my_module_path := $(my_multilib_module_path) else my_module_path := $(strip $(LOCAL_MODULE_PATH)) endif my_module_path := $(patsubst %/,%,$(my_module_path)) my_module_relative_path := $(strip $(LOCAL_MODULE_RELATIVE_PATH)) ifdef LOCAL_IS_HOST_MODULE partition_tag := actual_partition_tag := else ifeq (true,$(strip $(LOCAL_VENDOR_MODULE))) partition_tag := _VENDOR # A vendor module could be on the vendor partition at "vendor" or the system # partition at "system/vendor". actual_partition_tag := $(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system) else ifeq (true,$(strip $(LOCAL_OEM_MODULE))) partition_tag := _OEM actual_partition_tag := oem else ifeq (true,$(strip $(LOCAL_ODM_MODULE))) partition_tag := _ODM # An ODM module could be on the odm partition at "odm", the vendor partition # at "vendor/odm", or the system partition at "system/vendor/odm". actual_partition_tag := $(if $(filter true,$(BOARD_USES_ODMIMAGE)),odm,$(if $(filter true,$(BOARD_USES_VENDORIMAGE)),vendor,system)) else ifeq (true,$(strip $(LOCAL_PRODUCT_MODULE))) partition_tag := _PRODUCT # A product module could be on the product partition at "product" or the # system partition at "system/product". actual_partition_tag := $(if $(filter true,$(BOARD_USES_PRODUCTIMAGE)),product,system) else ifeq (true,$(strip $(LOCAL_SYSTEM_EXT_MODULE))) partition_tag := _SYSTEM_EXT # A system_ext-specific module could be on the system_ext partition at # "system_ext" or the system partition at "system/system_ext". actual_partition_tag := $(if $(filter true,$(BOARD_USES_SYSTEM_EXTIMAGE)),system_ext,system) else ifeq (NATIVE_TESTS,$(LOCAL_MODULE_CLASS)) partition_tag := _DATA actual_partition_tag := data else # The definition of should-install-to-system will be different depending # on which goal (e.g., sdk or just droid) is being built. partition_tag := $(if $(call should-install-to-system,$(LOCAL_MODULE_TAGS)),,_DATA) actual_partition_tag := $(if $(partition_tag),data,system) endif endif # if this is a soong module, verify that LOCAL_COMPATIBILITY_SUITE (legacy) matches # LOCAL_SOONG_PROVIDER_TEST_SUITES (new, via TestSuiteInfoProvider instead of AndroidMk stuff), # modulo "null-sute", "mts", and "mcts". mts/mcts are automatically added if there's a different # suite starting with "m(c)ts-". null-suite seems useless and is sometimes automatically added # if no other suites are added. ifneq (,$(filter $(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))) a := $(filter-out null-suite mts mcts,$(sort $(LOCAL_COMPATIBILITY_SUITE))) b := $(filter-out null-suite mts mcts,$(sort $(LOCAL_SOONG_PROVIDER_TEST_SUITES))) ifneq ($(a),$(b)) $(error $(LOCAL_MODULE): LOCAL_COMPATIBILITY_SUITE did not match LOCAL_SOONG_PROVIDER_TEST_SUITES$(newline) LOCAL_COMPATIBILITY_SUITE: $(a)$(newline) LOCAL_SOONG_PROVIDER_TEST_SUITES: $(b)$(newline)) endif a := b := endif # For test modules that lack a suite tag, set null-suite as the default. # We only support adding a default suite to native tests, native benchmarks, and instrumentation tests. # This is because they are the only tests we currently auto-generate test configs for. ifndef LOCAL_COMPATIBILITY_SUITE ifneq ($(filter NATIVE_TESTS NATIVE_BENCHMARK, $(LOCAL_MODULE_CLASS)),) LOCAL_COMPATIBILITY_SUITE := null-suite endif ifneq ($(filter APPS, $(LOCAL_MODULE_CLASS)),) ifneq ($(filter $(LOCAL_MODULE_TAGS),tests),) LOCAL_COMPATIBILITY_SUITE := null-suite endif endif endif use_testcase_folder := ifeq ($(my_module_path),) ifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES))) ifdef LOCAL_COMPATIBILITY_SUITE ifneq (true, $(LOCAL_IS_HOST_MODULE)) use_testcase_folder := true endif endif endif endif ifeq ($(LOCAL_IS_UNIT_TEST),true) ifeq ($(LOCAL_IS_HOST_MODULE),true) LOCAL_COMPATIBILITY_SUITE += host-unit-tests endif endif ifeq ($(my_module_path),) install_path_var := $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT$(partition_tag)_$(LOCAL_MODULE_CLASS) ifeq (true,$(LOCAL_PRIVILEGED_MODULE)) install_path_var := $(install_path_var)_PRIVILEGED endif my_module_path := $($(install_path_var)) # If use_testcase_folder be set, and LOCAL_MODULE_PATH not set, # overwrite the default path under testcase. ifeq ($(use_testcase_folder),true) arch_dir := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) testcase_folder := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)/$(arch_dir) my_module_path := $(testcase_folder) arch_dir := endif ifeq ($(strip $(my_module_path)),) $(error $(LOCAL_PATH): unhandled install path "$(install_path_var) for $(LOCAL_MODULE)") endif endif ifneq ($(my_module_relative_path),) my_module_path := $(my_module_path)/$(my_module_relative_path) endif endif # not LOCAL_UNINSTALLABLE_MODULE ifneq ($(strip $(LOCAL_BUILT_MODULE)$(LOCAL_INSTALLED_MODULE)),) $(error $(LOCAL_PATH): LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles) endif my_register_name := $(LOCAL_MODULE) ifeq ($(my_host_cross),true) my_register_name := host_cross_$(LOCAL_MODULE) endif ifdef LOCAL_2ND_ARCH_VAR_PREFIX ifndef LOCAL_NO_2ND_ARCH_MODULE_SUFFIX my_register_name := $(my_register_name)$($(my_prefix)2ND_ARCH_MODULE_SUFFIX) endif endif ifeq ($(my_host_cross),true) my_all_targets := host_cross_$(my_register_name)_all_targets else ifneq ($(LOCAL_IS_HOST_MODULE),) my_all_targets := host_$(my_register_name)_all_targets else my_all_targets := device_$(my_register_name)_all_targets endif # Make sure that this IS_HOST/CLASS/MODULE combination is unique. module_id := MODULE.$(if \ $(LOCAL_IS_HOST_MODULE),$($(my_prefix)OS),TARGET).$(LOCAL_MODULE_CLASS).$(my_register_name) ifdef $(module_id) $(error $(LOCAL_PATH): $(module_id) already defined by $($(module_id))) endif $(module_id) := $(LOCAL_PATH) # These are the same as local-intermediates-dir / local-generated-sources dir, but faster intermediates.COMMON := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates ifneq (,$(filter $(my_prefix)$(LOCAL_MODULE_CLASS),$(COMMON_MODULE_CLASSES))) intermediates := $($(my_prefix)OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates generated_sources_dir := $($(my_prefix)OUT_COMMON_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates else ifneq (,$(filter $(LOCAL_MODULE_CLASS),$(PER_ARCH_MODULE_CLASSES))) intermediates := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates else intermediates := $($(my_prefix)OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates endif generated_sources_dir := $($(my_prefix)OUT_GEN)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates endif ifneq ($(LOCAL_OVERRIDES_MODULES),) ifndef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_MODULE_CLASS),EXECUTABLES) EXECUTABLES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) else ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) SHARED_LIBRARIES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) else ifeq ($(LOCAL_MODULE_CLASS),ETC) ETC.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_MODULES)) else $(call pretty-error,LOCAL_MODULE_CLASS := $(LOCAL_MODULE_CLASS) cannot use LOCAL_OVERRIDES_MODULES) endif else $(call pretty-error,host modules cannot use LOCAL_OVERRIDES_MODULES) endif endif ########################################################### # Pick a name for the intermediate and final targets ########################################################### include $(BUILD_SYSTEM)/configure_module_stem.mk LOCAL_BUILT_MODULE := $(intermediates)/$(my_built_module_stem) ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error, LOCAL_MODULE_MAKEFILE can only be used from $(SOONG_ANDROID_MK)) endif # Use the install path requested by Soong. LOCAL_INSTALLED_MODULE := $(LOCAL_SOONG_INSTALLED_MODULE) else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) # Apk and its attachments reside in its own subdir. ifeq ($(LOCAL_MODULE_CLASS),APPS) # framework-res.apk doesn't like the additional layer. ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) # Neither do Runtime Resource Overlay apks, which contain just the overlaid resources. else ifeq ($(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY),true) else ifneq ($(use_testcase_folder),true) my_module_path := $(my_module_path)/$(LOCAL_MODULE) endif endif endif LOCAL_INSTALLED_MODULE := $(my_module_path)/$(my_installed_module_stem) endif # Assemble the list of targets to create PRIVATE_ variables for. LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE) ########################################################### ## Create .toc files from shared objects to reduce unnecessary rebuild # .toc files have the list of external dynamic symbols without their addresses. # As .KATI_RESTAT is specified to .toc files and commit-change-for-toc is used, # dependent binaries of a .toc file will be rebuilt only when the content of # the .toc file is changed. # # Don't create .toc files for Soong shared libraries, that is handled in # Soong and soong_cc_prebuilt.mk ########################################################### ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE).toc $(LOCAL_BUILT_MODULE).toc: $(LOCAL_BUILT_MODULE) $(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@.tmp) $(call commit-change-for-toc,$@) # Kati adds restat=1 to ninja. GNU make does nothing for this. .KATI_RESTAT: $(LOCAL_BUILT_MODULE).toc # Build .toc file when using mm, mma, or make $(my_register_name) $(my_all_targets): $(LOCAL_BUILT_MODULE).toc endif endif ########################################################### ## logtags: Add .logtags files to global list ########################################################### logtags_sources := $(filter %.logtags,$(LOCAL_SRC_FILES)) $(LOCAL_LOGTAGS_FILES) ifneq ($(strip $(logtags_sources) $(LOCAL_SOONG_LOGTAGS_FILES)),) event_log_tags := $(foreach f,$(LOCAL_SOONG_LOGTAGS_FILES) $(addprefix $(LOCAL_PATH)/,$(logtags_sources)),$(call clean-path,$(f))) else event_log_tags := endif ########################################################### ## make clean- targets ########################################################### cleantarget := clean-$(my_register_name) .PHONY: $(cleantarget) $(cleantarget) : PRIVATE_MODULE := $(my_register_name) $(cleantarget) : PRIVATE_CLEAN_FILES := \ $(LOCAL_BUILT_MODULE) \ $(LOCAL_INSTALLED_MODULE) \ $(intermediates) $(cleantarget):: @echo "Clean: $(PRIVATE_MODULE)" $(hide) rm -rf $(PRIVATE_CLEAN_FILES) ########################################################### ## Common definitions for module. ########################################################### $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PREFIX := $(my_prefix) $(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_name=$(LOCAL_MODULE) ifeq ($(LOCAL_MODULE_CLASS),) $(error "$(LOCAL_MODULE) in $(LOCAL_PATH) does not set $(LOCAL_MODULE_CLASS)") else $(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_type=$(LOCAL_MODULE_CLASS) endif $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_INTERMEDIATES_DIR:= $(intermediates) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX) # Tell the module and all of its sub-modules who it is. $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_MODULE:= $(my_register_name) # Provide a short-hand for building this module. # We name both BUILT and INSTALLED in case # LOCAL_UNINSTALLABLE_MODULE is set. .PHONY: $(my_all_targets) $(my_all_targets): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE) $(LOCAL_ADDITIONAL_CHECKED_MODULE) .PHONY: $(my_register_name) $(my_register_name): $(my_all_targets) ifneq ($(my_register_name),$(LOCAL_MODULE)) # $(LOCAL_MODULE) covers all the multilib targets. .PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE) : $(my_all_targets) endif # Set up phony targets that covers all modules under the given paths. # This allows us to build everything in given paths by running mmma/mma. define my_path_comp parent := $(patsubst %/,%,$(dir $(1))) parent_target := MODULES-IN-$$(subst /,-,$$(parent)) .PHONY: $$(parent_target) $$(parent_target): $(2) ifndef $$(parent_target) $$(parent_target) := true ifneq (,$$(findstring /,$$(parent))) $$(eval $$(call my_path_comp,$$(parent),$$(parent_target))) endif endif endef _local_path := $(patsubst %/,%,$(LOCAL_PATH)) _local_path_target := MODULES-IN-$(subst /,-,$(_local_path)) .PHONY: $(_local_path_target) $(_local_path_target): $(my_register_name) ifndef $(_local_path_target) $(_local_path_target) := true ifneq (,$(findstring /,$(_local_path))) $(eval $(call my_path_comp,$(_local_path),$(_local_path_target))) endif endif _local_path := _local_path_target := my_path_comp := ########################################################### ## Module installation rule ########################################################### my_installed_symlinks := ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) # Soong already generated the copy rule, but make the installed location depend on the Make # copy of the intermediates for now, as some rules that collect intermediates may expect # them to exist. $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD) $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) @echo "Install: $@" ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(copy-file-or-link-to-new-target) else $(copy-file-to-new-target) endif $(PRIVATE_POST_INSTALL_CMD) # Rule to install the module's companion symlinks my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix))) $(foreach symlink,$(my_installed_symlinks),\ $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink))\ $(call declare-0p-target,$(symlink))) $(my_all_targets) : | $(my_installed_symlinks) endif # !LOCAL_UNINSTALLABLE_MODULE # Add dependencies on LOCAL_SOONG_INSTALL_SYMLINKS if we're installing any kind of module, not just # ones that set LOCAL_SOONG_INSTALLED_MODULE. This is so we can have a soong module that only # installs symlinks (e.g. install_symlink). We can't set LOCAL_SOONG_INSTALLED_MODULE to a symlink # because cp commands will fail on symlinks. ifneq (,$(or $(LOCAL_SOONG_INSTALLED_MODULE),$(call boolean-not,$(LOCAL_UNINSTALLABLE_MODULE)))) $(foreach symlink, $(LOCAL_SOONG_INSTALL_SYMLINKS), $(call declare-0p-target,$(symlink))) $(my_all_targets) : | $(LOCAL_SOONG_INSTALL_SYMLINKS) endif ########################################################### ## VINTF manifest fragment and init.rc goals ########################################################### my_vintf_installed:= my_vintf_path:= my_vintf_pairs:= my_init_rc_installed := my_init_rc_path := my_init_rc_pairs := ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) ifndef LOCAL_IS_HOST_MODULE # Rule to install the module's companion vintf fragments. ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),) my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS) else my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml)) endif ifneq ($(strip $(my_vintf_fragments)),) # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery # have init.rc files that need to be installed alongside them. Manually handle the case where the # output file is in the recovery partition. my_vintf_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC)) my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(my_vintf_path)/vintf/manifest/$(notdir $(xml))) my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml))) # Only set up copy rules once, even if another arch variant shares it my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs)) ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs) ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs)) $(my_all_targets) : $(my_vintf_installed) # Install fragments together with the target $(LOCAL_INSTALLED_MODULE) : | $(my_vintf_installed) endif endif # my_vintf_fragments # Rule to install the module's companion init.rc. ifneq ($(strip $(LOCAL_FULL_INIT_RC)),) my_init_rc := $(LOCAL_FULL_INIT_RC) else my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc)) endif ifneq ($(strip $(my_init_rc)),) # Make doesn't support recovery or ramdisk as an output partition, # but some Soong modules installed in recovery or ramdisk # have init.rc files that need to be installed alongside them. # Manually handle the case where the # output file is in the recovery or ramdisk partition. ifneq (,$(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path))) ifneq (,$(filter $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/%,$(my_module_path))) my_init_rc_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/system/etc else my_init_rc_path := $(TARGET_RECOVERY_ROOT_OUT)/system/etc endif else ifneq (,$(filter $(TARGET_RAMDISK_OUT)/%,$(my_module_path))) my_init_rc_path := $(TARGET_RAMDISK_OUT)/system/etc else my_init_rc_path := $(TARGET_OUT$(partition_tag)_ETC) endif my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc))) my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc))) # Make sure we only set up the copy rules once, even if another arch variant # shares a common LOCAL_INIT_RC. my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs)) ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs) ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs)) $(my_all_targets) : $(my_init_rc_installed) # Install init_rc together with the target $(LOCAL_INSTALLED_MODULE) : | $(my_init_rc_installed) endif endif # my_init_rc endif # !LOCAL_IS_HOST_MODULE endif # !LOCAL_UNINSTALLABLE_MODULE ########################################################### ## CHECK_BUILD goals ########################################################### my_checked_module := # If nobody has defined a more specific module for the # checked modules, use LOCAL_BUILT_MODULE. ifdef LOCAL_CHECKED_MODULE my_checked_module := $(LOCAL_CHECKED_MODULE) else my_checked_module := $(LOCAL_BUILT_MODULE) endif my_checked_module += $(LOCAL_ADDITIONAL_CHECKED_MODULE) # If they request that this module not be checked, then don't. # PLEASE DON'T SET THIS. ANY PLACES THAT SET THIS WITHOUT # GOOD REASON WILL HAVE IT REMOVED. ifdef LOCAL_DONT_CHECK_MODULE my_checked_module := endif # Don't check build target module defined for the 2nd arch ifndef LOCAL_IS_HOST_MODULE ifdef LOCAL_2ND_ARCH_VAR_PREFIX my_checked_module := endif endif ########################################################### ## Test Data ########################################################### my_test_data_pairs := my_installed_test_data := # Source to relative dst file paths for reuse in LOCAL_COMPATIBILITY_SUITE. my_test_data_file_pairs := ifneq ($(strip $(filter NATIVE_TESTS,$(LOCAL_MODULE_CLASS)) $(LOCAL_IS_FUZZ_TARGET)),) ifneq ($(strip $(LOCAL_TEST_DATA)),) ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) # Soong LOCAL_TEST_DATA is of the form :: # or :, to be installed to # // or /, # respectively. ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) define copy_test_data_pairs _src_base := $$(call word-colon,1,$$(td)) _file := $$(call word-colon,2,$$(td)) _relative_install_path := $$(call word-colon,3,$$(td)) ifeq (,$$(_relative_install_path)) _relative_dest_file := $$(_file) else _relative_dest_file := $$(call append-path,$$(_relative_install_path),$$(_file)) endif my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_relative_dest_file)) my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_relative_dest_file) endef else define copy_test_data_pairs _src_base := $$(call word-colon,1,$$(td)) _file := $$(call word-colon,2,$$(td)) ifndef _file _file := $$(_src_base) _src_base := $$(LOCAL_PATH) endif ifneq (,$$(findstring ..,$$(_file))) $$(call pretty-error,LOCAL_TEST_DATA may not include '..': $$(_file)) endif ifneq (,$$(filter/%,$$(_src_base) $$(_file))) $$(call pretty-error,LOCAL_TEST_DATA may not include absolute paths: $$(_src_base) $$(_file)) endif my_test_data_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(call append-path,$$(my_module_path),$$(_file)) my_test_data_file_pairs += $$(call append-path,$$(_src_base),$$(_file)):$$(_file) endef endif $(foreach td,$(LOCAL_TEST_DATA),$(eval $(copy_test_data_pairs))) copy_test_data_pairs := ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) my_installed_test_data := $(call copy-many-files,$(my_test_data_pairs)) $(LOCAL_INSTALLED_MODULE): $(my_installed_test_data) else # Skip installing test data for Soong modules, it's already been handled. # Just compute my_installed_test_data. my_installed_test_data := $(foreach f, $(my_test_data_pairs), $(call word-colon,2,$(f))) endif endif endif endif ########################################################### ## SOONG INSTALL PAIRS ########################################################### # Declare dependencies for LOCAL_SOONG_INSTALL_PAIRS in soong to the module it relies on. ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) $(my_all_targets): \ $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\ $(word 2,$(subst :,$(space),$(f)))) endif ########################################################### ## Compatibility suite files. ########################################################### ifdef LOCAL_COMPATIBILITY_SUITE ifneq (,$(LOCAL_FULL_TEST_CONFIG)) test_config := $(LOCAL_FULL_TEST_CONFIG) else ifneq (,$(LOCAL_TEST_CONFIG)) test_config := $(LOCAL_PATH)/$(LOCAL_TEST_CONFIG) else test_config := $(wildcard $(LOCAL_PATH)/AndroidTest.xml) endif ifeq ($(EXCLUDE_MCTS),true) ifeq (,$(filter $(LOCAL_MODULE),$(mcts_whitelist))) ifneq (,$(test_config)) ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE))) LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE)) endif endif endif endif ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) ifeq ($(EXCLUDE_MCTS),true) ifneq (,$(test_config)) ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE))) LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE)) endif endif endif # If we are building a native test or benchmark and its stem variants are not defined, # separate the multiple architectures into subdirectories of the testcase folder. arch_dir := is_native := multi_arch := ifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) is_native := true multi_arch := true endif ifdef LOCAL_MULTILIB multi_arch := true # These conditionals allow this functionality to be mimicked in Soong else ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) multi_arch := true endif endif ifdef multi_arch arch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) else ifeq ($(use_testcase_folder),true) arch_dir := /$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) endif endif multi_arch := my_default_test_module := my_default_test_module := $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(arch_dir)/$(my_installed_module_stem) ifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module)) # Install into the testcase folder $(LOCAL_INSTALLED_MODULE) : $(my_default_test_module) endif # The module itself. $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) := $(patsubst %:$(LOCAL_INSTALLED_MODULE),$(LOCAL_INSTALLED_MODULE):$(LOCAL_INSTALLED_MODULE),\ $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ $(LOCAL_BUILT_MODULE):$(dir)/$(my_installed_module_stem)))) \ $(eval my_compat_module_arch_dir_$(suite).$(my_register_name) :=) \ $(foreach dir,$(call compatibility_suite_dirs,$(suite),$(arch_dir)),$(eval my_compat_module_arch_dir_$(suite).$(my_register_name) += $(dir))) \ $(eval my_compat_dist_config_$(suite) := )) ifneq (,$(LOCAL_SOONG_CLASSES_JAR)) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_api_map_$(suite) += $(LOCAL_SOONG_CLASSES_JAR))) endif # Auto-generate build config. ifeq (,$(test_config)) ifneq (true,$(is_native)) is_instrumentation_test := true ifeq (true, $(LOCAL_IS_HOST_MODULE)) is_instrumentation_test := false endif # If LOCAL_MODULE_CLASS is not APPS, it's certainly not an instrumentation # test. However, some packages for test data also have LOCAL_MODULE_CLASS # set to APPS. These will require flag LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG # to disable auto-generating test config file. ifneq (APPS, $(LOCAL_MODULE_CLASS)) is_instrumentation_test := false endif endif # CTS modules can be used for test data, so test config files must be # explicitly created using AndroidTest.xml ifeq (,$(filter cts, $(LOCAL_COMPATIBILITY_SUITE))) ifneq (true, $(LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG)) ifeq (true, $(filter true,$(is_native) $(is_instrumentation_test))) include $(BUILD_SYSTEM)/autogen_test_config.mk test_config := $(autogen_test_config_file) autogen_test_config_file := endif endif endif endif is_instrumentation_test := # Currently this flag variable is true only for the `android_test_helper_app` type module # which should not have any .config file ifeq (true, $(LOCAL_DISABLE_TEST_CONFIG)) test_config := endif # Make sure we only add the files once for multilib modules. ifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files # Sync the auto_test_config value for multilib modules. ifdef $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_autogen ALL_MODULES.$(my_register_name).auto_test_config := true endif else $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files := true # LOCAL_COMPATIBILITY_SUPPORT_FILES is a list of [:]. $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \ $(eval p := $(subst :,$(space),$(f))) \ $(eval s := $(word 1,$(p))) \ $(eval n := $(or $(word 2,$(p)),$(notdir $(word 1, $(p))))) \ $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(s):$(dir)/$(n))))) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_api_map_$(suite) += $(foreach f, $(LOCAL_COMPATIBILITY_SUPPORT_FILES), \ $(eval p := $(subst :,$(space),$(f))) \ $(eval s := $(word 1,$(p))) \ $(if $(filter %.apk,$(s)) $(filter %.jar,$(s)),$(s),)))) ifneq (,$(test_config)) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(test_config):$(dir)/$(LOCAL_MODULE).config$(LOCAL_TEST_CONFIG_SUFFIX)))) endif ifneq (,$(LOCAL_EXTRA_FULL_TEST_CONFIGS)) $(foreach test_config_file, $(LOCAL_EXTRA_FULL_TEST_CONFIGS), \ $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(test_config_file):$(dir)/$(basename $(notdir $(test_config_file))).config)))) endif ifneq (,$(wildcard $(LOCAL_PATH)/DynamicConfig.xml)) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_config_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(LOCAL_PATH)/DynamicConfig.xml:$(dir)/$(LOCAL_MODULE).dynamic))) endif endif # $(my_prefix)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_compat_files ifeq ($(use_testcase_folder),true) ifneq ($(my_test_data_file_pairs),) # Filter out existng installed test data paths when collecting test data files to be installed and # indexed as they cause build rule conflicts. Instead put them in a separate list which is only # used for indexing. $(foreach pair, $(my_test_data_file_pairs), \ $(eval parts := $(subst :,$(space),$(pair))) \ $(eval src_path := $(word 1,$(parts))) \ $(eval file := $(word 2,$(parts))) \ $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ $(call filter-copy-pair,$(src_path),$(call append-path,$(dir),$(file)),$(my_installed_test_data)))) \ $(eval my_compat_dist_test_data_$(suite) += \ $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ $(filter $(my_installed_test_data),$(call append-path,$(dir),$(file))))) \ $(eval my_compat_api_map_$(suite) += \ $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),)))) endif else ifneq ($(my_test_data_file_pairs),) $(foreach pair, $(my_test_data_file_pairs), \ $(eval parts := $(subst :,$(space),$(pair))) \ $(eval src_path := $(word 1,$(parts))) \ $(eval file := $(word 2,$(parts))) \ $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) += $(foreach dir, $(call compatibility_suite_dirs,$(suite),$(arch_dir)), \ $(src_path):$(call append-path,$(dir),$(file)))) \ $(eval my_compat_api_map_$(suite) += \ $(if $(filter %.apk,$(src_path)) $(filter %.jar,$(src_path)),$(src_path),)))) endif endif arch_dir := is_native := $(call create-suite-dependencies) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_config_$(suite) := ) \ $(eval my_compat_dist_test_data_$(suite) := ) \ $(eval my_compat_api_map_$(suite) := )) endif # LOCAL_UNINSTALLABLE_MODULE # HACK: pretend a soong LOCAL_FULL_TEST_CONFIG is autogenerated by setting the flag in # module-info.json # TODO: (b/113029686) Add explicit flag from Soong to determine if a test was # autogenerated. ifneq (,$(filter $(SOONG_OUT_DIR)%,$(LOCAL_FULL_TEST_CONFIG))) ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) ALL_MODULES.$(my_register_name).auto_test_config := true endif endif endif # LOCAL_COMPATIBILITY_SUITE my_supported_variant := ifeq ($(my_host_cross),true) my_supported_variant := HOST_CROSS else ifdef LOCAL_IS_HOST_MODULE my_supported_variant := HOST else my_supported_variant := DEVICE endif endif ########################################################### ## Register with ALL_MODULES ########################################################### ifndef ALL_MODULES.$(my_register_name).PATH # These keys are no longer used, they've been replaced by keys that specify # target/host/host_cross (REQUIRED_FROM_TARGET / REQUIRED_FROM_HOST) and similar. # # Marking them obsolete to ensure that anyone using these internal variables looks for # alternates. $(KATI_obsolete_var ALL_MODULES.$(my_register_name).REQUIRED) $(KATI_obsolete_var ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED) $(KATI_obsolete_var ALL_MODULES.$(my_register_name).HOST_REQUIRED) $(KATI_obsolete_var ALL_MODULES.$(my_register_name).TARGET_REQUIRED) endif ALL_MODULES += $(my_register_name) # Don't use += on subvars, or else they'll end up being # recursively expanded. ALL_MODULES.$(my_register_name).CLASS := \ $(ALL_MODULES.$(my_register_name).CLASS) $(LOCAL_MODULE_CLASS) ALL_MODULES.$(my_register_name).PATH := \ $(ALL_MODULES.$(my_register_name).PATH) $(LOCAL_PATH) ALL_MODULES.$(my_register_name).TAGS := \ $(ALL_MODULES.$(my_register_name).TAGS) $(LOCAL_MODULE_TAGS) ALL_MODULES.$(my_register_name).CHECKED := \ $(ALL_MODULES.$(my_register_name).CHECKED) $(my_checked_module) ALL_MODULES.$(my_register_name).BUILT := \ $(ALL_MODULES.$(my_register_name).BUILT) $(LOCAL_BUILT_MODULE) ALL_MODULES.$(my_register_name).SOONG_MODULE_TYPE := \ $(ALL_MODULES.$(my_register_name).SOONG_MODULE_TYPE) $(LOCAL_SOONG_MODULE_TYPE) ALL_MODULES.$(my_register_name).IS_SOONG_MODULE := \ $(if $(filter $(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)),true) ifndef LOCAL_IS_HOST_MODULE ALL_MODULES.$(my_register_name).TARGET_BUILT := \ $(ALL_MODULES.$(my_register_name).TARGET_BUILT) $(LOCAL_BUILT_MODULE) endif ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE)) # Store the list of paths to installed locations of files provided by this # module. Used as dependencies of the image packaging rules when the module # is installed by the current product. ALL_MODULES.$(my_register_name).INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \ $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\ $(word 2,$(subst :,$(space),$(f)))) \ $(LOCAL_SOONG_INSTALL_SYMLINKS) \ $(my_init_rc_installed) \ $(my_installed_test_data) \ $(my_vintf_installed)) ALL_MODULES.$(my_register_name).INSTALLED_SYMLINKS := $(LOCAL_SOONG_INSTALL_SYMLINKS) # Store the list of colon-separated pairs of the built and installed locations # of files provided by this module. Used by custom packaging rules like # package-modules.mk that need to copy the built files to a custom install # location during packaging. # # Translate copies from $(LOCAL_PREBUILT_MODULE_FILE) to $(LOCAL_BUILT_MODULE) # so that package-modules.mk gets any transtive dependencies added to # $(LOCAL_BUILT_MODULE), for example unstripped symbols files. ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \ $(patsubst $(LOCAL_PREBUILT_MODULE_FILE):%,$(LOCAL_BUILT_MODULE):%,$(LOCAL_SOONG_INSTALL_PAIRS)) \ $(my_init_rc_pairs) \ $(my_test_data_pairs) \ $(my_vintf_pairs)) # Store the list of vintf/init_rc as order-only dependencies ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \ $(my_init_rc_installed) \ $(my_vintf_installed)) else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) ALL_MODULES.$(my_register_name).INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \ $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks) \ $(my_installed_test_data) $(my_vintf_installed)) ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \ $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \ $(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs)) ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \ $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \ $(my_init_rc_installed) \ $(my_vintf_installed)) endif # Mark LOCAL_SOONG_INSTALL_SYMLINKS as installed if we're installing any kind of module, not just # ones that set LOCAL_SOONG_INSTALLED_MODULE. This is so we can have a soong module that only # installs symlinks (e.g. installed_symlink). We can't set LOCAL_SOONG_INSTALLED_MODULE to a symlink # because cp commands will fail on symlinks. ifneq (,$(or $(LOCAL_SOONG_INSTALLED_MODULE),$(call boolean-not,$(LOCAL_UNINSTALLABLE_MODULE)))) ALL_MODULES.$(my_register_name).INSTALLED += $(LOCAL_SOONG_INSTALL_SYMLINKS) ALL_MODULES.$(my_register_name).INSTALLED_SYMLINKS := $(LOCAL_SOONG_INSTALL_SYMLINKS) endif ifdef LOCAL_PICKUP_FILES # Files or directories ready to pick up by the build system # when $(LOCAL_BUILT_MODULE) is done. ALL_MODULES.$(my_register_name).PICKUP_FILES := \ $(ALL_MODULES.$(my_register_name).PICKUP_FILES) $(LOCAL_PICKUP_FILES) endif # Record the platform availability of this module. Note that the availability is not # meaningful for non-installable modules (e.g., static libs) or host modules. # We only care about modules that are installable to the device. ifeq (true,$(LOCAL_NOT_AVAILABLE_FOR_PLATFORM)) ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) ifndef LOCAL_IS_HOST_MODULE ALL_MODULES.$(my_register_name).NOT_AVAILABLE_FOR_PLATFORM := true endif endif endif my_required_modules := $(LOCAL_REQUIRED_MODULES) \ $(LOCAL_REQUIRED_MODULES_$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) ifdef LOCAL_IS_HOST_MODULE my_required_modules += $(LOCAL_REQUIRED_MODULES_$($(my_prefix)OS)) endif ifdef LOCAL_ACONFIG_FILES ALL_MODULES.$(my_register_name).ACONFIG_FILES := \ $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES) endif ifdef LOCAL_FILESYSTEM_FILELIST ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST := \ $(ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST) $(LOCAL_FILESYSTEM_FILELIST) endif ifndef LOCAL_SOONG_MODULE_INFO_JSON ALL_MAKE_MODULE_INFO_JSON_MODULES += $(my_register_name) ALL_MODULES.$(my_register_name).SHARED_LIBS := \ $(ALL_MODULES.$(my_register_name).SHARED_LIBS) $(LOCAL_SHARED_LIBRARIES) ALL_MODULES.$(my_register_name).STATIC_LIBS := \ $(ALL_MODULES.$(my_register_name).STATIC_LIBS) $(LOCAL_STATIC_LIBRARIES) ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \ $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES) ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES := \ $(ALL_MODULES.$(my_register_name).LOCAL_RUNTIME_LIBRARIES) $(LOCAL_RUNTIME_LIBRARIES) \ $(LOCAL_JAVA_LIBRARIES) ALL_MODULES.$(my_register_name).LOCAL_STATIC_LIBRARIES := \ $(ALL_MODULES.$(my_register_name).LOCAL_STATIC_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES) ifneq ($(my_test_data_file_pairs),) # Export the list of targets that are handled as data inputs and required # by tests at runtime. The format of my_test_data_file_pairs is # is $(path):$(relative_file) but for module-info, only the string after # ":" is needed. ALL_MODULES.$(my_register_name).TEST_DATA := \ $(strip $(ALL_MODULES.$(my_register_name).TEST_DATA) \ $(foreach f, $(my_test_data_file_pairs),\ $(call word-colon,2,$(f)))) endif ifdef LOCAL_TEST_DATA_BINS ALL_MODULES.$(my_register_name).TEST_DATA_BINS := \ $(ALL_MODULES.$(my_register_name).TEST_DATA_BINS) $(LOCAL_TEST_DATA_BINS) endif ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS := \ $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS) \ $(filter-out $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS),$(my_supported_variant)) ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \ $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) $(LOCAL_COMPATIBILITY_SUITE) ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE) ALL_MODULES.$(my_register_name).TEST_CONFIG := $(test_config) ALL_MODULES.$(my_register_name).EXTRA_TEST_CONFIGS := $(LOCAL_EXTRA_FULL_TEST_CONFIGS) ALL_MODULES.$(my_register_name).TEST_MAINLINE_MODULES := $(LOCAL_TEST_MAINLINE_MODULES) ifdef LOCAL_IS_UNIT_TEST ALL_MODULES.$(my_register_name).IS_UNIT_TEST := $(LOCAL_IS_UNIT_TEST) endif ifdef LOCAL_TEST_OPTIONS_TAGS ALL_MODULES.$(my_register_name).TEST_OPTIONS_TAGS := $(LOCAL_TEST_OPTIONS_TAGS) endif ########################################################## # Track module-level dependencies. # (b/204397180) Unlock RECORD_ALL_DEPS was acknowledged reasonable for better Atest performance. ALL_MODULES.$(my_register_name).ALL_DEPS := \ $(ALL_MODULES.$(my_register_name).ALL_DEPS) \ $(LOCAL_STATIC_LIBRARIES) \ $(LOCAL_WHOLE_STATIC_LIBRARIES) \ $(LOCAL_SHARED_LIBRARIES) \ $(LOCAL_DYLIB_LIBRARIES) \ $(LOCAL_RLIB_LIBRARIES) \ $(LOCAL_PROC_MACRO_LIBRARIES) \ $(LOCAL_HEADER_LIBRARIES) \ $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(LOCAL_JAVA_LIBRARIES) \ $(LOCAL_JNI_SHARED_LIBRARIES) endif ALL_MODULES.$(my_register_name).TEST_MODULE_CONFIG_BASE := $(LOCAL_TEST_MODULE_CONFIG_BASE) ########################################################################## ## When compiling against API imported module, use API import stub ## libraries. ########################################################################## ifneq ($(call module-in-vendor-or-product),) ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) apiimport_postfix := .apiimport ifeq ($(LOCAL_IN_PRODUCT),true) apiimport_postfix := .apiimport.product else apiimport_postfix := .apiimport.vendor endif my_required_modules := $(foreach l,$(my_required_modules), \ $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) endif endif ########################################################################## ## When compiling against the VNDK, add the .vendor or .product suffix to ## required modules. ########################################################################## ifneq ($(call module-in-vendor-or-product),) ##################################################### ## Soong modules may be built three times, once for ## /system, once for /vendor and once for /product. ## If we're using the VNDK, switch all soong ## libraries over to the /vendor or /product variant. ##################################################### ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) # We don't do this renaming for soong-defined modules since they already # have correct names (with .vendor or .product suffix when necessary) in # their LOCAL_*_LIBRARIES. ifeq ($(LOCAL_IN_PRODUCT),true) my_required_modules := $(foreach l,$(my_required_modules),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) else my_required_modules := $(foreach l,$(my_required_modules),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) endif endif endif ifdef LOCAL_IS_HOST_MODULE ifneq ($(my_host_cross),true) ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST := \ $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST) $(my_required_modules)) ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST := \ $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST)\ $(my_required_modules)) ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST := \ $(strip $(ALL_MODULES.$(my_register_name).TARGET_REQUIRED_FROM_HOST)\ $(LOCAL_TARGET_REQUIRED_MODULES)) else ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS := \ $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_HOST_CROSS) $(my_required_modules)) ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS := \ $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_HOST_CROSS)\ $(my_required_modules)) ifdef LOCAL_TARGET_REQUIRED_MODULES $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from host_cross modules) endif endif ifdef LOCAL_HOST_REQUIRED_MODULES $(call pretty-error,LOCAL_HOST_REQUIRED_MODULES may not be used from host modules. Use LOCAL_REQUIRED_MODULES instead) endif else ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET := \ $(strip $(ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET) $(my_required_modules)) ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET := \ $(strip $(ALL_MODULES.$(my_register_name).EXPLICITLY_REQUIRED_FROM_TARGET)\ $(my_required_modules)) ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET := \ $(strip $(ALL_MODULES.$(my_register_name).HOST_REQUIRED_FROM_TARGET)\ $(LOCAL_HOST_REQUIRED_MODULES)) ifdef LOCAL_TARGET_REQUIRED_MODULES $(call pretty-error,LOCAL_TARGET_REQUIRED_MODULES may not be used from target modules. Use LOCAL_REQUIRED_MODULES instead) endif endif ifdef event_log_tags ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS := \ $(ALL_MODULES.$(my_register_name).EVENT_LOG_TAGS) $(event_log_tags) endif ALL_MODULES.$(my_register_name).MAKEFILE := \ $(ALL_MODULES.$(my_register_name).MAKEFILE) $(LOCAL_MODULE_MAKEFILE) ifdef LOCAL_MODULE_OWNER ALL_MODULES.$(my_register_name).OWNER := \ $(sort $(ALL_MODULES.$(my_register_name).OWNER) $(LOCAL_MODULE_OWNER)) endif ifdef LOCAL_2ND_ARCH_VAR_PREFIX ALL_MODULES.$(my_register_name).FOR_2ND_ARCH := true endif ALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross) ifndef LOCAL_IS_HOST_MODULE ALL_MODULES.$(my_register_name).APEX_KEYS_FILE := $(LOCAL_APEX_KEY_PATH) endif test_config := INSTALLABLE_FILES.$(LOCAL_INSTALLED_MODULE).MODULE := $(my_register_name) ########################################################### ## umbrella targets used to verify builds ########################################################### j_or_n := ifneq (,$(filter EXECUTABLES SHARED_LIBRARIES STATIC_LIBRARIES HEADER_LIBRARIES NATIVE_TESTS RLIB_LIBRARIES DYLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS))) j_or_n := native else ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS))) j_or_n := java endif endif ifdef LOCAL_IS_HOST_MODULE h_or_t := host ifeq ($(my_host_cross),true) h_or_hc_or_t := host-cross else h_or_hc_or_t := host endif else h_or_hc_or_t := target h_or_t := target endif ifdef j_or_n $(j_or_n) $(h_or_t) $(j_or_n)-$(h_or_hc_or_t) : $(my_checked_module) ifneq (,$(filter $(LOCAL_MODULE_TAGS),tests)) $(j_or_n)-$(h_or_t)-tests $(j_or_n)-tests $(h_or_t)-tests : $(my_checked_module) endif $(LOCAL_MODULE)-$(h_or_hc_or_t) : $(my_all_targets) .PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t) ifeq ($(j_or_n),native) $(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix) : $(my_all_targets) .PHONY: $(LOCAL_MODULE)-$(h_or_hc_or_t)$(my_32_64_bit_suffix) endif endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=base_rules)) ########################################################### # Ensure privileged applications always have LOCAL_PRIVILEGED_MODULE ########################################################### ifndef LOCAL_PRIVILEGED_MODULE ifneq (,$(filter $(TARGET_OUT_APPS_PRIVILEGED)/% $(TARGET_OUT_VENDOR_APPS_PRIVILEGED)/%,$(my_module_path))) LOCAL_PRIVILEGED_MODULE := true endif endif ########################################################### ## NOTICE files ########################################################### include $(BUILD_NOTICE_FILE) ########################################################### ## SBOM generation ########################################################### include $(BUILD_SBOM_GEN) ================================================ FILE: core/binary.mk ================================================ ########################################################### ## Standard rules for building binary object files from ## asm/c/cpp/yacc/lex/etc source files. ## ## The list of object files is exported in $(all_objects). ########################################################### ####################################### include $(BUILD_SYSTEM)/base_rules.mk include $(BUILD_SYSTEM)/use_lld_setup.mk ####################################### ################################################## # Compute the dependency of the shared libraries ################################################## # On the target, we compile with -nostdlib, so we must add in the # default system shared libraries, unless they have requested not # to by supplying a LOCAL_SYSTEM_SHARED_LIBRARIES value. One would # supply that, for example, when building libc itself. ifdef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) ifdef USE_HOST_MUSL my_system_shared_libraries := libc_musl else my_system_shared_libraries := endif else my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) endif else ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) my_system_shared_libraries := libc libm libdl else my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries)) endif endif # Third party code has additional no-override flags. is_third_party := ifneq ($(filter external/% hardware/% vendor/%,$(LOCAL_PATH)),) is_third_party := true endif my_soong_problems := # The following LOCAL_ variables will be modified in this file. # Because the same LOCAL_ variables may be used to define modules for both 1st arch and 2nd arch, # we can't modify them in place. my_src_files := $(LOCAL_SRC_FILES) my_src_files_exclude := $(LOCAL_SRC_FILES_EXCLUDE) my_static_libraries := $(LOCAL_STATIC_LIBRARIES) my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES) my_shared_libraries := $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES)) my_header_libraries := $(LOCAL_HEADER_LIBRARIES) my_cflags := $(LOCAL_CFLAGS) my_conlyflags := $(LOCAL_CONLYFLAGS) my_cppflags := $(LOCAL_CPPFLAGS) my_cflags_no_override := $(GLOBAL_CLANG_CFLAGS_NO_OVERRIDE) my_cppflags_no_override := $(GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE) ifeq ($(my_32_64_bit_suffix), 64) my_cflags_no_override += $(GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE) endif ifdef is_third_party my_cflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE) my_cppflags_no_override += $(GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE) endif my_ldflags := $(LOCAL_LDFLAGS) my_ldlibs := $(LOCAL_LDLIBS) my_asflags := $(LOCAL_ASFLAGS) my_cc := $(LOCAL_CC) my_cc_wrapper := $(CC_WRAPPER) my_cxx := $(LOCAL_CXX) my_cxx_link := $(LOCAL_CXX) my_cxx_ldlibs := my_cxx_wrapper := $(CXX_WRAPPER) my_c_includes := $(LOCAL_C_INCLUDES) my_generated_sources := $(LOCAL_GENERATED_SOURCES) my_additional_dependencies := $(LOCAL_ADDITIONAL_DEPENDENCIES) my_export_c_include_dirs := $(LOCAL_EXPORT_C_INCLUDE_DIRS) my_export_c_include_deps := $(LOCAL_EXPORT_C_INCLUDE_DEPS) my_arflags := # Disable clang-tidy if it is not found. ifeq ($(PATH_TO_CLANG_TIDY),) my_tidy_enabled := false else # If LOCAL_TIDY is not defined, use global WITH_TIDY my_tidy_enabled := $(LOCAL_TIDY) ifeq ($(my_tidy_enabled),) my_tidy_enabled := $(WITH_TIDY) endif endif # my_tidy_checks is empty if clang-tidy is disabled. my_tidy_checks := my_tidy_flags := ifneq (,$(filter 1 true,$(my_tidy_enabled))) # Set up global default checks my_tidy_checks := $(WITH_TIDY_CHECKS) ifeq ($(my_tidy_checks),) my_tidy_checks := $(call default_global_tidy_checks,$(LOCAL_PATH)) endif # Append local clang-tidy checks. ifneq ($(LOCAL_TIDY_CHECKS),) my_tidy_checks := $(my_tidy_checks),$(LOCAL_TIDY_CHECKS) endif my_tidy_flags := $(strip $(WITH_TIDY_FLAGS) $(LOCAL_TIDY_FLAGS)) # If tidy flags are not specified, default to check all header files. ifeq ($(my_tidy_flags),) my_tidy_flags := $(call default_tidy_header_filter,$(LOCAL_PATH)) endif # If clang-tidy is not enabled globally, add the -quiet flag. ifeq (,$(filter 1 true,$(WITH_TIDY))) my_tidy_flags += -quiet -extra-arg-before=-fno-caret-diagnostics endif ifneq ($(my_tidy_checks),) # We might be using the static analyzer through clang-tidy. # https://bugs.llvm.org/show_bug.cgi?id=32914 my_tidy_flags += -extra-arg-before=-D__clang_analyzer__ # A recent change in clang-tidy (r328258) enabled destructor inlining, # which appears to cause a number of false positives. Until that's # resolved, this turns off the effects of r328258. # https://bugs.llvm.org/show_bug.cgi?id=37459 my_tidy_flags += -extra-arg-before=-Xclang my_tidy_flags += -extra-arg-before=-analyzer-config my_tidy_flags += -extra-arg-before=-Xclang my_tidy_flags += -extra-arg-before=c++-temp-dtor-inlining=false endif endif my_tidy_checks := $(subst $(space),,$(my_tidy_checks)) # Configure the pool to use for clang rules. # If LOCAL_CC or LOCAL_CXX is set don't use goma or RBE. # If clang-tidy is being used, don't use the RBE pool (as clang-tidy runs in # the same action, and is not remoted) my_pool := ifeq (,$(strip $(my_cc))$(strip $(my_cxx))$(strip $(my_tidy_checks))) my_pool := $(GOMA_OR_RBE_POOL) endif ifneq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH))))) ifeq (,$(strip $(foreach dir,$(NATIVE_COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH))))) my_native_coverage := true my_clang_coverage := true else my_native_coverage := false my_clang_coverage := false endif else my_native_coverage := false my_clang_coverage := false endif ifneq ($(NATIVE_COVERAGE),true) my_native_coverage := false endif ifneq ($(CLANG_COVERAGE),true) my_clang_coverage := false endif # Exclude directories from checking allowed manual binder interface lists. # TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths. ifneq (,$(filter $(addsuffix %,$(ALLOWED_MANUAL_INTERFACE_PATHS)),$(LOCAL_PATH))) my_cflags += -DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES endif my_allow_undefined_symbols := $(strip $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)) ifdef SANITIZE_HOST ifdef LOCAL_IS_HOST_MODULE my_allow_undefined_symbols := true endif endif my_ndk_sysroot := my_ndk_sysroot_lib := my_api_level := 10000 my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) ifneq ($(LOCAL_SDK_VERSION),) ifdef LOCAL_IS_HOST_MODULE $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module) endif # Make sure we've built the NDK. my_additional_dependencies += $(SOONG_OUT_DIR)/ndk_base.timestamp my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION) # Historically we've just set up a bunch of symlinks in prebuilts/ndk to map # missing API levels to existing ones where necessary, but we're not doing # that for the generated libraries. Clip the API level to the minimum where # appropriate. my_ndk_api := $(LOCAL_SDK_VERSION) ifneq ($(my_ndk_api),current) my_ndk_api := $(call math_max,$(my_ndk_api),$(my_min_sdk_version)) endif my_ndk_crt_version := $(my_ndk_api) ifneq ($(my_ndk_api),current) my_api_level := $(my_ndk_api) endif my_built_ndk := $(SOONG_OUT_DIR)/ndk my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE) my_ndk_sysroot := $(my_built_ndk)/sysroot my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib/$(my_ndk_triple)/$(my_ndk_api) # The bionic linker now has support for packed relocations and gnu style # hashes (which are much faster!), but shipping to older devices requires # the old style hash. Fortunately, we can build with both and it'll work # anywhere. my_ldflags += -Wl,--hash-style=both # We don't want to expose the relocation packer to the NDK just yet. LOCAL_PACK_MODULE_RELOCATIONS := false # Set up the NDK stl variant. Starting from NDK-r5 the c++ stl resides in a separate location. # See ndk/docs/CPLUSPLUS-SUPPORT.html my_ndk_stl_include_path := my_ndk_stl_shared_lib_fullpath := my_ndk_stl_static_lib := my_cpu_variant := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)CPU_ABI) LOCAL_NDK_STL_VARIANT := $(strip $(LOCAL_NDK_STL_VARIANT)) ifeq (,$(LOCAL_NDK_STL_VARIANT)) LOCAL_NDK_STL_VARIANT := system endif ifneq (1,$(words $(filter none system c++_static c++_shared, $(LOCAL_NDK_STL_VARIANT)))) $(error $(LOCAL_PATH): Unknown LOCAL_NDK_STL_VARIANT $(LOCAL_NDK_STL_VARIANT)) endif ifeq (system,$(LOCAL_NDK_STL_VARIANT)) my_ndk_source_root := \ $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources my_ndk_stl_include_path := $(my_ndk_source_root)/cxx-stl/system/include my_system_shared_libraries += libstdc++ else ifneq (,$(filter c++_%, $(LOCAL_NDK_STL_VARIANT))) my_llvm_dir := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION) my_libcxx_arch_dir := $(my_llvm_dir)/android_libc++/ndk/$($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR) # Include the target-specific __config_site file followed by the generic libc++ headers. my_ndk_stl_include_path := $(my_libcxx_arch_dir)/include/c++/v1 my_ndk_stl_include_path += $(my_llvm_dir)/include/c++/v1 my_libcxx_libdir := $(my_libcxx_arch_dir)/lib ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT)) my_ndk_stl_static_lib := \ $(my_libcxx_libdir)/libc++_static.a \ $(my_libcxx_libdir)/libc++abi.a else my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so endif my_ndk_stl_static_lib += $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_LIBUNWIND) my_ldlibs += -ldl else # LOCAL_NDK_STL_VARIANT must be none # Do nothing. endif # Clang's coverage/profile runtime needs symbols like 'stderr' that were not # exported from libc prior to API level 23 ifneq ($(my_ndk_api),current) ifeq ($(call math_lt, $(my_ndk_api),23),true) my_native_coverage := false my_clang_coverage := false endif endif endif ifneq ($(LOCAL_MIN_SDK_VERSION),) ifdef LOCAL_IS_HOST_MODULE $(error $(LOCAL_PATH): LOCAL_MIN_SDK_VERSION cannot be used in host module) endif my_api_level := $(LOCAL_MIN_SDK_VERSION) endif ifeq ($(NATIVE_COVERAGE),true) ifndef LOCAL_IS_HOST_MODULE my_ldflags += -Wl,--wrap,getenv ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES) ifeq ($(LOCAL_SDK_VERSION),) my_whole_static_libraries += libprofile-extras else my_whole_static_libraries += libprofile-extras_ndk endif endif endif endif ifeq ($(CLANG_COVERAGE),true) ifndef LOCAL_IS_HOST_MODULE my_ldflags += $(CLANG_COVERAGE_HOST_LDFLAGS) ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES) my_whole_static_libraries += libclang_rt.profile ifeq ($(LOCAL_SDK_VERSION),) my_whole_static_libraries += libprofile-clang-extras else my_whole_static_libraries += libprofile-clang-extras_ndk endif endif endif ifeq ($(my_clang_coverage),true) my_profile_instr_generate := $(CLANG_COVERAGE_INSTR_PROFILE) ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) my_cflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS) my_ldflags += $(CLANG_COVERAGE_CONTINUOUS_FLAGS) endif my_profile_instr_generate += $(CLANG_COVERAGE_CONFIG_COMMFLAGS) my_cflags += $(CLANG_COVERAGE_INSTR_PROFILE) $(CLANG_COVERAGE_CONFIG_CFLAGS) $(CLANG_COVERAGE_CONFIG_COMMFLAGS) my_ldflags += $(CLANG_COVERAGE_CONFIG_COMMFLAGS) ifneq ($(filter hwaddress,$(my_sanitize)),) my_cflags += $(CLANG_COVERAGE_HWASAN_FLAGS) my_ldflags += $(CLANG_COVERAGE_HWASAN_FLAGS) endif endif endif ifneq ($(call module-in-vendor-or-product),) my_cflags += -D__ANDROID_VNDK__ ifneq ($(LOCAL_IN_VENDOR),) # Vendor modules have LOCAL_IN_VENDOR my_cflags += -D__ANDROID_VENDOR__ else ifneq ($(LOCAL_IN_PRODUCT),) # Product modules have LOCAL_IN_PRODUCT my_cflags += -D__ANDROID_PRODUCT__ endif # Define __ANDROID_VENDOR_API__ for both product and vendor variants because # they both use the same LLNDK libraries. ifeq ($(BOARD_API_LEVEL),) # TODO(b/314036847): This is a fallback for UDC targets. # This must be a build failure when UDC is no longer built from this source tree. my_cflags += -D__ANDROID_VENDOR_API__=$(PLATFORM_SDK_VERSION) else my_cflags += -D__ANDROID_VENDOR_API__=$(BOARD_API_LEVEL) endif endif ifndef LOCAL_IS_HOST_MODULE # For device libraries, move LOCAL_LDLIBS references to my_shared_libraries. We # no longer need to use my_ldlibs to pick up NDK prebuilt libraries since we're # linking my_shared_libraries by full path now. my_allowed_ldlibs := # Sort ldlibs and ldflags between -l and other linker flags # We'll do this again later, since there are still changes happening, but that's fine. my_ldlib_flags := $(my_ldflags) $(my_ldlibs) my_ldlibs := $(filter -l%,$(my_ldlib_flags)) my_ldflags := $(filter-out -l%,$(my_ldlib_flags)) my_ldlib_flags := # Move other ldlibs back to shared libraries my_shared_libraries += $(patsubst -l%,lib%,$(filter-out $(my_allowed_ldlibs),$(my_ldlibs))) my_ldlibs := $(filter $(my_allowed_ldlibs),$(my_ldlibs)) else # LOCAL_IS_HOST_MODULE # Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of # device builds ifndef USE_HOST_MUSL my_ldlibs += -ldl -lpthread -lm ifneq ($(HOST_OS),darwin) my_ldlibs += -lrt endif endif endif ifneq ($(LOCAL_SDK_VERSION),) my_all_ndk_libraries := $(NDK_KNOWN_LIBS) my_ndk_shared_libraries := \ $(filter $(my_all_ndk_libraries),\ $(my_shared_libraries) $(my_system_shared_libraries)) my_shared_libraries := \ $(filter-out $(my_all_ndk_libraries),$(my_shared_libraries)) my_system_shared_libraries := \ $(filter-out $(my_all_ndk_libraries),$(my_system_shared_libraries)) endif # MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because # all code is position independent, and then those warnings get promoted to # errors. ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) my_cflags += -fPIE ifndef BUILD_HOST_static ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_ldflags += -pie endif endif else my_cflags += -fPIC endif ifdef LOCAL_IS_HOST_MODULE my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)OS)) $(LOCAL_SRC_FILES_$($(my_prefix)OS)_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) my_static_libraries += $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)OS)) my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)OS)) my_header_libraries += $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)OS)) my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)OS)) my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)OS)) my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)OS)) my_ldlibs += $(LOCAL_LDLIBS_$($(my_prefix)OS)) my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)OS)) my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)OS)) my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)OS)) endif my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_$(my_32_64_bit_suffix)) my_src_files_exclude += $(LOCAL_SRC_FILES_EXCLUDE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SRC_FILES_EXCLUDE_$(my_32_64_bit_suffix)) my_shared_libraries += $(LOCAL_SHARED_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_SHARED_LIBRARIES_$(my_32_64_bit_suffix)) my_cflags += $(LOCAL_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CFLAGS_$(my_32_64_bit_suffix)) my_cppflags += $(LOCAL_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CPPFLAGS_$(my_32_64_bit_suffix)) my_ldflags += $(LOCAL_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_LDFLAGS_$(my_32_64_bit_suffix)) my_asflags += $(LOCAL_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_ASFLAGS_$(my_32_64_bit_suffix)) my_c_includes += $(LOCAL_C_INCLUDES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_C_INCLUDES_$(my_32_64_bit_suffix)) my_generated_sources += $(LOCAL_GENERATED_SOURCES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_GENERATED_SOURCES_$(my_32_64_bit_suffix)) my_missing_exclude_files := $(filter-out $(my_src_files),$(my_src_files_exclude)) ifneq ($(my_missing_exclude_files),) $(warning Files are listed in LOCAL_SRC_FILES_EXCLUDE but not LOCAL_SRC_FILES) $(error $(my_missing_exclude_files)) endif my_src_files := $(filter-out $(my_src_files_exclude),$(my_src_files)) # Strip '/' from the beginning of each src file. This helps the ../ detection in case # the source file is in the form of /../file my_src_files := $(patsubst /%,%,$(my_src_files)) my_clang := $(strip $(LOCAL_CLANG)) ifdef LOCAL_CLANG_$(my_32_64_bit_suffix) my_clang := $(strip $(LOCAL_CLANG_$(my_32_64_bit_suffix))) endif ifdef LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) my_clang := $(strip $(LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) endif ifeq ($(my_clang),false) $(call pretty-error,LOCAL_CLANG false is no longer supported) endif ifeq ($(LOCAL_C_STD),) my_c_std_version := $(DEFAULT_C_STD_VERSION) else ifeq ($(LOCAL_C_STD),experimental) my_c_std_version := $(EXPERIMENTAL_C_STD_VERSION) else my_c_std_version := $(LOCAL_C_STD) endif ifeq ($(LOCAL_CPP_STD),) my_cpp_std_version := $(DEFAULT_CPP_STD_VERSION) else ifeq ($(LOCAL_CPP_STD),experimental) my_cpp_std_version := $(EXPERIMENTAL_CPP_STD_VERSION) else my_cpp_std_version := $(LOCAL_CPP_STD) endif my_c_std_conlyflags := my_cpp_std_cppflags := ifneq (,$(my_c_std_version)) my_c_std_conlyflags := -std=$(my_c_std_version) endif ifneq (,$(my_cpp_std_version)) my_cpp_std_cppflags := -std=$(my_cpp_std_version) endif # Extra cflags for projects under external/ directory ifneq ($(filter external/%,$(LOCAL_PATH)),) my_cflags += $(CLANG_EXTERNAL_CFLAGS) endif # Extra cflags for projects under hardware/ directory. # This should match the definition of `thirdPartyDirPrefixExceptions` # in build/soong/android/paths.go. # Get the second element of LOCAL_PATH ifneq ($(filter hardware/%,$(LOCAL_PATH)),) my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH))) must_compile_hardware_subdirs := \ hardware/google/% \ hardware/interfaces/% \ hardware/libhardware/% \ hardware/libhardware_legacy/% \ hardware/ril/% ifeq ($(filter $(must_compile_hardware_subdirs),$(my_subdir)),) my_cflags += $(CLANG_EXTERNAL_CFLAGS) endif endif # Extra cflags for projects under vendor/ directory. # This should match the definition of `thirdPartyDirPrefixExceptions` # in build/soong/android/paths.go. ifneq ($(filter vendor/%,$(LOCAL_PATH)),) my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH))) # Do not add the flags for any subdir that contains the string "google". ifneq ($(findstring google,$(my_subdir)),) my_cflags += $(CLANG_EXTERNAL_CFLAGS) endif endif # arch-specific static libraries go first so that generic ones can depend on them my_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries) my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries) my_header_libraries := $(LOCAL_HEADER_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_HEADER_LIBRARIES_$(my_32_64_bit_suffix)) $(my_header_libraries) include $(BUILD_SYSTEM)/cxx_stl_setup.mk ifneq ($(strip $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)),) my_linker := $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER) else my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LINKER) endif include $(BUILD_SYSTEM)/config_sanitizers.mk ifneq ($(filter ../%,$(my_src_files)),) my_soong_problems += dotdot_srcs endif ifneq ($(foreach i,$(my_c_includes),$(filter %/..,$(i))$(findstring /../,$(i))),) my_soong_problems += dotdot_incs endif ########################################################### ## Explicitly declare assembly-only __ASSEMBLY__ macro for ## assembly source ########################################################### my_asflags += -D__ASSEMBLY__ ########################################################### # TODO: support a mix of standard extensions so that this isn't necessary LOCAL_CPP_EXTENSION := $(strip $(LOCAL_CPP_EXTENSION)) ifeq ($(LOCAL_CPP_EXTENSION),) LOCAL_CPP_EXTENSION := .cpp endif # Certain modules like libdl have to have symbols resolved at runtime and blow # up if --no-undefined is passed to the linker. ifeq ($(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)),) ifeq ($(my_allow_undefined_symbols),) ifneq ($(HOST_OS),darwin) my_ldflags += -Wl,--no-undefined endif else ifdef LOCAL_IS_HOST_MODULE ifeq ($(HOST_OS),darwin) # darwin defaults to treating undefined symbols as errors my_ldflags += -Wl,-undefined,dynamic_lookup endif endif endif endif ifeq (true,$(LOCAL_GROUP_STATIC_LIBRARIES)) $(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES := true else $(LOCAL_BUILT_MODULE): PRIVATE_GROUP_STATIC_LIBRARIES := endif ########################################################### ## Define arm-vs-thumb-mode flags. ########################################################### LOCAL_ARM_MODE := $(strip $(LOCAL_ARM_MODE)) ifeq ($($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),arm) normal_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),thumb) # Read the values from something like TARGET_arm_CFLAGS or # TARGET_thumb_CFLAGS. HOST_(arm|thumb)_CFLAGS values aren't # actually used (although they are usually empty). normal_objects_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)$(normal_objects_mode)_CFLAGS) else normal_objects_mode := normal_objects_cflags := endif ########################################################### ## Define per-module debugging flags. Users can turn on ## debugging for a particular module by setting DEBUG_MODULE_ModuleName ## to a non-empty value in their environment or buildspec.mk, ## and setting HOST_/TARGET_CUSTOM_DEBUG_CFLAGS to the ## debug flags that they want to use. ########################################################### ifdef DEBUG_MODULE_$(strip $(LOCAL_MODULE)) debug_cflags := $($(my_prefix)CUSTOM_DEBUG_CFLAGS) else debug_cflags := endif #################################################### ## Keep track of src -> obj mapping #################################################### my_tracked_gen_files := my_tracked_src_files := ########################################################### ## Stuff source generated from one-off tools ########################################################### $(my_generated_sources): PRIVATE_MODULE := $(my_register_name) my_gen_sources_copy := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(filter $(generated_sources_dir)/%,$(my_generated_sources))) $(my_gen_sources_copy): $(intermediates)/% : $(generated_sources_dir)/% @echo "Copy: $@" $(copy-file-to-target) my_generated_sources := $(patsubst $(generated_sources_dir)/%,$(intermediates)/%,$(my_generated_sources)) # Generated sources that will actually produce object files. # Other files (like headers) are allowed in LOCAL_GENERATED_SOURCES, # since other compiled sources may depend on them, and we set up # the dependencies. my_gen_src_files := $(filter %.c %$(LOCAL_CPP_EXTENSION) %.S %.s,$(my_generated_sources)) #################################################### ## Compile RenderScript with reflected C++ #################################################### renderscript_sources := $(filter %.rscript %.fs,$(my_src_files)) ifneq (,$(renderscript_sources)) my_soong_problems += rs renderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources)) RenderScript_file_stamp := $(intermediates)/RenderScriptCPP.stamp renderscript_intermediate := $(intermediates)/renderscript renderscript_target_api := ifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API)) renderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API) else ifneq (,$(LOCAL_SDK_VERSION)) # Set target-api for LOCAL_SDK_VERSIONs other than current. ifneq (,$(filter-out current system_current test_current, $(LOCAL_SDK_VERSION))) renderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) endif endif # LOCAL_SDK_VERSION is set endif # LOCAL_RENDERSCRIPT_TARGET_API is set ifeq ($(LOCAL_RENDERSCRIPT_CC),) LOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC) endif # Turn on all warnings and warnings as errors for RS compiles. # This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error renderscript_flags := -Wall -Werror renderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS) # -m32 or -m64 renderscript_flags += -m$(my_32_64_bit_suffix) renderscript_includes := \ $(TOPDIR)external/clang/lib/Headers \ $(TOPDIR)frameworks/rs/script_api/include \ $(LOCAL_RENDERSCRIPT_INCLUDES) ifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),) renderscript_includes := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE) endif bc_dep_files := $(addprefix $(renderscript_intermediate)/, \ $(patsubst %.fs,%.d, $(patsubst %.rscript,%.d, $(notdir $(renderscript_sources))))) $(RenderScript_file_stamp): PRIVATE_RS_INCLUDES := $(renderscript_includes) $(RenderScript_file_stamp): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC) $(RenderScript_file_stamp): PRIVATE_RS_FLAGS := $(renderscript_flags) $(RenderScript_file_stamp): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath) $(RenderScript_file_stamp): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate) $(RenderScript_file_stamp): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api)) $(RenderScript_file_stamp): PRIVATE_DEP_FILES := $(bc_dep_files) $(RenderScript_file_stamp): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC) $(transform-renderscripts-to-cpp-and-bc) # include the dependency files (.d) generated by llvm-rs-cc. $(call include-depfile,$(RenderScript_file_stamp).d,$(RenderScript_file_stamp)) LOCAL_INTERMEDIATE_TARGETS += $(RenderScript_file_stamp) rs_generated_cpps := $(addprefix \ $(renderscript_intermediate)/ScriptC_,$(patsubst %.fs,%.cpp, $(patsubst %.rscript,%.cpp, \ $(notdir $(renderscript_sources))))) $(call track-src-file-gen,$(renderscript_sources),$(rs_generated_cpps)) # This is just a no-op rule to make sure gmake doesn't skip updating the dependents. $(rs_generated_cpps) : $(RenderScript_file_stamp) @echo "Updated RS generated cpp file $@." $(hide) touch $@ my_c_includes += $(renderscript_intermediate) my_generated_sources += $(rs_generated_cpps) endif ########################################################### ## Compile the .proto files to .cc (or .c) and then to .o ########################################################### ifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),) LOCAL_PROTOC_OPTIMIZE_TYPE := lite endif proto_sources := $(filter %.proto,$(my_src_files)) ifneq ($(proto_sources),) proto_gen_dir := $(generated_sources_dir)/proto proto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources)) my_rename_cpp_ext := ifneq (,$(filter nanopb-c nanopb-c-enable_malloc nanopb-c-16bit nanopb-c-enable_malloc-16bit nanopb-c-32bit nanopb-c-enable_malloc-32bit, $(LOCAL_PROTOC_OPTIMIZE_TYPE))) my_proto_source_suffix := .c my_proto_c_includes := external/nanopb-c my_protoc_flags := --nanopb_out=$(proto_gen_dir) \ --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb my_protoc_deps := $(NANOPB_SRCS) $(proto_sources_fullpath:%.proto=%.options) else my_proto_source_suffix := $(LOCAL_CPP_EXTENSION) ifneq ($(my_proto_source_suffix),.cc) # aprotoc is hardcoded to write out only .cc file. # We need to rename the extension to $(LOCAL_CPP_EXTENSION) if it's not .cc. my_rename_cpp_ext := true endif my_proto_c_includes := external/protobuf/src my_cflags += -DGOOGLE_PROTOBUF_NO_RTTI my_protoc_flags := --cpp_out=$(if $(filter lite lite-static,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite:,)$(proto_gen_dir) my_protoc_deps := endif my_proto_c_includes += $(proto_gen_dir) proto_generated_cpps := $(addprefix $(proto_gen_dir)/, \ $(patsubst %.proto,%.pb$(my_proto_source_suffix),$(proto_sources_fullpath))) # Ensure the transform-proto-to-cc rule is only defined once in multilib build. ifndef $(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined $(proto_generated_cpps): PRIVATE_PROTO_INCLUDES := $(TOP) $(proto_generated_cpps): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS) $(my_protoc_flags) $(proto_generated_cpps): PRIVATE_RENAME_CPP_EXT := $(my_rename_cpp_ext) $(proto_generated_cpps): $(proto_gen_dir)/%.pb$(my_proto_source_suffix): %.proto $(my_protoc_deps) $(PROTOC) $(transform-proto-to-cc) $(my_host)$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined := true endif # Ideally we can generate the source directly into $(intermediates). # But many Android.mks assume the .pb.hs are in $(generated_sources_dir). # As a workaround, we make a copy in the $(intermediates). proto_intermediate_dir := $(intermediates)/proto proto_intermediate_cpps := $(patsubst $(proto_gen_dir)/%,$(proto_intermediate_dir)/%,\ $(proto_generated_cpps)) $(proto_intermediate_cpps) : $(proto_intermediate_dir)/% : $(proto_gen_dir)/% @echo "Copy: $@" $(copy-file-to-target) $(hide) cp $(basename $<).h $(basename $@).h $(call track-src-file-gen,$(proto_sources),$(proto_intermediate_cpps)) my_generated_sources += $(proto_intermediate_cpps) my_c_includes += $(my_proto_c_includes) # Auto-export the generated proto source dir. my_export_c_include_dirs += $(my_proto_c_includes) ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc) my_static_libraries += libprotobuf-c-nano-enable_malloc else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c) my_static_libraries += libprotobuf-c-nano else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-16bit) my_static_libraries += libprotobuf-c-nano-enable_malloc-16bit else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-16bit) my_static_libraries += libprotobuf-c-nano-16bit else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-enable_malloc-32bit) my_static_libraries += libprotobuf-c-nano-enable_malloc-32bit else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nanopb-c-32bit) my_static_libraries += libprotobuf-c-nano-32bit else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full) ifdef LOCAL_SDK_VERSION my_static_libraries += libprotobuf-cpp-full-ndk else my_shared_libraries += libprotobuf-cpp-full endif else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),lite-static) my_static_libraries += libprotobuf-cpp-lite else ifdef LOCAL_SDK_VERSION my_static_libraries += libprotobuf-cpp-lite-ndk else my_shared_libraries += libprotobuf-cpp-lite endif endif endif # $(proto_sources) non-empty ########################################################### ## AIDL: Compile .aidl files to .cpp and .h files ########################################################### aidl_src := $(strip $(filter %.aidl,$(my_src_files))) aidl_gen_cpp := ifneq ($(aidl_src),) # Use the intermediates directory to avoid writing our own .cpp -> .o rules. aidl_gen_cpp_root := $(intermediates)/aidl-generated/src aidl_gen_include_root := $(intermediates)/aidl-generated/include # Multi-architecture builds have distinct intermediates directories. # Thus we'll actually generate source for each architecture. $(foreach s,$(aidl_src),\ $(eval $(call define-aidl-cpp-rule,$(s),$(aidl_gen_cpp_root),aidl_gen_cpp))) $(foreach cpp,$(aidl_gen_cpp), \ $(call include-depfile,$(addsuffix .aidl.d,$(basename $(cpp))),$(cpp))) $(call track-src-file-gen,$(aidl_src),$(aidl_gen_cpp)) $(aidl_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE) $(aidl_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(aidl_gen_include_root) $(aidl_gen_cpp) : PRIVATE_AIDL_FLAGS := $(addprefix -I,$(LOCAL_AIDL_INCLUDES)) # Add generated headers to include paths. my_c_includes += $(aidl_gen_include_root) my_export_c_include_dirs += $(aidl_gen_include_root) # Pick up the generated C++ files later for transformation to .o files. my_generated_sources += $(aidl_gen_cpp) endif # $(aidl_src) non-empty ########################################################### ## Compile the .vts files to .cc (or .c) and then to .o ########################################################### vts_src := $(strip $(filter %.vts,$(my_src_files))) vts_gen_cpp := ifneq ($(vts_src),) my_soong_problems += vts # Use the intermediates directory to avoid writing our own .cpp -> .o rules. vts_gen_cpp_root := $(intermediates)/vts-generated/src vts_gen_include_root := $(intermediates)/vts-generated/include # Multi-architecture builds have distinct intermediates directories. # Thus we'll actually generate source for each architecture. $(foreach s,$(vts_src),\ $(eval $(call define-vts-cpp-rule,$(s),$(vts_gen_cpp_root),vts_gen_cpp))) $(call track-src-file-gen,$(vts_src),$(vts_gen_cpp)) $(vts_gen_cpp) : PRIVATE_MODULE := $(LOCAL_MODULE) $(vts_gen_cpp) : PRIVATE_HEADER_OUTPUT_DIR := $(vts_gen_include_root) $(vts_gen_cpp) : PRIVATE_VTS_FLAGS := $(addprefix -I,$(LOCAL_VTS_INCLUDES)) $(addprefix -m,$(LOCAL_VTS_MODE)) # Add generated headers to include paths. my_c_includes += $(vts_gen_include_root) my_export_c_include_dirs += $(vts_gen_include_root) # Pick up the generated C++ files later for transformation to .o files. my_generated_sources += $(vts_gen_cpp) endif # $(vts_src) non-empty ########################################################### ## YACC: Compile .y/.yy files to .c/.cpp and then to .o. ########################################################### y_yacc_sources := $(filter %.y,$(my_src_files)) y_yacc_cs := $(addprefix \ $(intermediates)/,$(y_yacc_sources:.y=.c)) ifneq ($(y_yacc_cs),) $(y_yacc_cs): $(intermediates)/%.c: \ $(TOPDIR)$(LOCAL_PATH)/%.y $(BISON) $(BISON_DATA) $(M4) \ $(my_additional_dependencies) $(call transform-y-to-c-or-cpp) $(call track-src-file-gen,$(y_yacc_sources),$(y_yacc_cs)) my_generated_sources += $(y_yacc_cs) endif yy_yacc_sources := $(filter %.yy,$(my_src_files)) yy_yacc_cpps := $(addprefix \ $(intermediates)/,$(yy_yacc_sources:.yy=$(LOCAL_CPP_EXTENSION))) ifneq ($(yy_yacc_cpps),) $(yy_yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \ $(TOPDIR)$(LOCAL_PATH)/%.yy $(BISON) $(BISON_DATA) $(M4) \ $(my_additional_dependencies) $(call transform-y-to-c-or-cpp) $(call track-src-file-gen,$(yy_yacc_sources),$(yy_yacc_cpps)) my_generated_sources += $(yy_yacc_cpps) endif ########################################################### ## LEX: Compile .l/.ll files to .c/.cpp and then to .o. ########################################################### l_lex_sources := $(filter %.l,$(my_src_files)) l_lex_cs := $(addprefix \ $(intermediates)/,$(l_lex_sources:.l=.c)) ifneq ($(l_lex_cs),) $(l_lex_cs): $(LEX) $(M4) $(l_lex_cs): $(intermediates)/%.c: \ $(TOPDIR)$(LOCAL_PATH)/%.l $(transform-l-to-c-or-cpp) $(call track-src-file-gen,$(l_lex_sources),$(l_lex_cs)) my_generated_sources += $(l_lex_cs) endif ll_lex_sources := $(filter %.ll,$(my_src_files)) ll_lex_cpps := $(addprefix \ $(intermediates)/,$(ll_lex_sources:.ll=$(LOCAL_CPP_EXTENSION))) ifneq ($(ll_lex_cpps),) $(ll_lex_cpps): $(LEX) $(M4) $(ll_lex_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \ $(TOPDIR)$(LOCAL_PATH)/%.ll $(transform-l-to-c-or-cpp) $(call track-src-file-gen,$(ll_lex_sources),$(ll_lex_cpps)) my_generated_sources += $(ll_lex_cpps) endif ########################################################### ## C++: Compile .cpp files to .o. ########################################################### ifneq ($(filter %$(LOCAL_CPP_EXTENSION).arm,$(my_src_files)),) $(call pretty-error,Files ending in $(LOCAL_CPP_EXTENSION).arm are deprecated. See $(CHANGES_URL)#file_arm) endif dotdot_sources := $(filter ../%$(LOCAL_CPP_EXTENSION),$(my_src_files)) dotdot_objects := $(foreach s,$(dotdot_sources),\ $(eval $(call compile-dotdot-cpp-file,$(s),\ $(my_additional_dependencies),\ dotdot_objects,\ $(my_pool)))) $(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects)) cpp_normal_sources := $(filter-out ../%,$(filter %$(LOCAL_CPP_EXTENSION),$(my_src_files))) cpp_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o)) $(call track-src-file-obj,$(cpp_normal_sources),$(cpp_objects)) $(dotdot_objects) $(cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(dotdot_objects) $(cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) ifneq ($(strip $(cpp_objects)),) $(cpp_objects): .KATI_NINJA_POOL := $(my_pool) $(cpp_objects): $(intermediates)/%.o: \ $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \ $(my_additional_dependencies) $(CLANG_CXX) $(transform-$(PRIVATE_HOST)cpp-to-o) $(call include-depfiles-for-objs, $(cpp_objects)) endif cpp_objects += $(dotdot_objects) ########################################################### ## C++: Compile generated .cpp files to .o. ########################################################### gen_cpp_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(my_generated_sources)) gen_cpp_objects := $(gen_cpp_sources:%$(LOCAL_CPP_EXTENSION)=%.o) $(call track-gen-file-obj,$(gen_cpp_sources),$(gen_cpp_objects)) ifneq ($(strip $(gen_cpp_objects)),) # Compile all generated files as thumb. $(gen_cpp_objects): .KATI_NINJA_POOL := $(my_pool) $(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) $(gen_cpp_objects): $(intermediates)/%.o: \ $(intermediates)/%$(LOCAL_CPP_EXTENSION) \ $(my_additional_dependencies) $(CLANG_CXX) $(transform-$(PRIVATE_HOST)cpp-to-o) $(call include-depfiles-for-objs, $(gen_cpp_objects)) endif ########################################################### ## S: Compile generated .S and .s files to .o. ########################################################### gen_S_sources := $(filter %.S,$(my_generated_sources)) gen_S_objects := $(gen_S_sources:%.S=%.o) $(call track-gen-file-obj,$(gen_S_sources),$(gen_S_objects)) ifneq ($(strip $(gen_S_sources)),) $(gen_S_objects): .KATI_NINJA_POOL := $(my_pool) $(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)s-to-o) $(call include-depfiles-for-objs, $(gen_S_objects)) endif gen_s_sources := $(filter %.s,$(my_generated_sources)) gen_s_objects := $(gen_s_sources:%.s=%.o) $(call track-gen-file-obj,$(gen_s_sources),$(gen_s_objects)) ifneq ($(strip $(gen_s_objects)),) $(gen_s_objects): .KATI_NINJA_POOL := $(my_pool) $(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)s-to-o) endif gen_asm_objects := $(gen_S_objects) $(gen_s_objects) $(gen_asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) ########################################################### ## o: Include generated .o files in output. ########################################################### gen_o_objects := $(filter %.o,$(my_generated_sources)) ########################################################### ## C: Compile .c files to .o. ########################################################### ifneq ($(filter %.c.arm,$(my_src_files)),) $(call pretty-error,Files ending in .c.arm are deprecated. See $(CHANGES_URL)#file_arm) endif dotdot_sources := $(filter ../%.c, $(my_src_files)) dotdot_objects := $(foreach s, $(dotdot_sources),\ $(eval $(call compile-dotdot-c-file,$(s),\ $(my_additional_dependencies),\ dotdot_objects,\ $(my_pool)))) $(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects)) c_normal_sources := $(filter-out ../%,$(filter %.c,$(my_src_files))) c_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o)) $(call track-src-file-obj,$(c_normal_sources),$(c_objects)) $(dotdot_objects) $(c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(dotdot_objects) $(c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) ifneq ($(strip $(c_objects)),) $(c_objects): .KATI_NINJA_POOL := $(my_pool) $(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)c-to-o) $(call include-depfiles-for-objs, $(c_objects)) endif c_objects += $(dotdot_objects) ########################################################### ## C: Compile generated .c files to .o. ########################################################### gen_c_sources := $(filter %.c,$(my_generated_sources)) gen_c_objects := $(gen_c_sources:%.c=%.o) $(call track-gen-file-obj,$(gen_c_sources),$(gen_c_objects)) ifneq ($(strip $(gen_c_objects)),) # Compile all generated files as thumb. $(gen_c_objects): .KATI_NINJA_POOL := $(my_pool) $(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) $(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)c-to-o) $(call include-depfiles-for-objs, $(gen_c_objects)) endif ########################################################### ## ObjC: Compile .m files to .o ########################################################### objc_sources := $(filter %.m,$(my_src_files)) objc_objects := $(addprefix $(intermediates)/,$(objc_sources:.m=.o)) $(call track-src-file-obj,$(objc_sources),$(objc_objects)) ifneq ($(strip $(objc_objects)),) my_soong_problems += objc $(objc_objects): .KATI_NINJA_POOL := $(my_pool) $(objc_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.m \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)m-to-o) $(call include-depfiles-for-objs, $(objc_objects)) endif ########################################################### ## ObjC++: Compile .mm files to .o ########################################################### objcpp_sources := $(filter %.mm,$(my_src_files)) objcpp_objects := $(addprefix $(intermediates)/,$(objcpp_sources:.mm=.o)) $(call track-src-file-obj,$(objcpp_sources),$(objcpp_objects)) ifneq ($(strip $(objcpp_objects)),) $(objcpp_objects): .KATI_NINJA_POOL := $(my_pool) $(objcpp_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.mm \ $(my_additional_dependencies) $(CLANG_CXX) $(transform-$(PRIVATE_HOST)mm-to-o) $(call include-depfiles-for-objs, $(objcpp_objects)) endif ########################################################### ## AS: Compile .S files to .o. ########################################################### asm_sources_S := $(filter %.S,$(my_src_files)) dotdot_sources := $(filter ../%,$(asm_sources_S)) asm_sources_S := $(filter-out ../%,$(asm_sources_S)) asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o)) $(call track-src-file-obj,$(asm_sources_S),$(asm_objects_S)) dotdot_objects_S := $(foreach s,$(dotdot_sources),\ $(eval $(call compile-dotdot-s-file,$(s),\ $(my_additional_dependencies),\ dotdot_objects_S,\ $(my_pool)))) $(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_S)) ifneq ($(strip $(asm_objects_S)),) $(asm_objects_S): .KATI_NINJA_POOL := $(my_pool) $(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)s-to-o) $(call include-depfiles-for-objs, $(asm_objects_S)) endif asm_sources_s := $(filter %.s,$(my_src_files)) dotdot_sources := $(filter ../%,$(asm_sources_s)) asm_sources_s := $(filter-out ../%,$(asm_sources_s)) asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o)) $(call track-src-file-obj,$(asm_sources_s),$(asm_objects_s)) dotdot_objects_s := $(foreach s,$(dotdot_sources),\ $(eval $(call compile-dotdot-s-file-no-deps,$(s),\ $(my_additional_dependencies),\ dotdot_objects_s,\ $(my_pool)))) $(call track-src-file-obj,$(dotdot_sources),$(dotdot_objects_s)) ifneq ($(strip $(asm_objects_s)),) $(asm_objects_s): .KATI_NINJA_POOL := $(my_pool) $(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s \ $(my_additional_dependencies) $(CLANG) $(transform-$(PRIVATE_HOST)s-to-o) endif asm_objects := $(dotdot_objects_S) $(dotdot_objects_s) $(asm_objects_S) $(asm_objects_s) $(asm_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) # .asm for x86/x86_64 needs to be compiled with yasm. asm_sources_asm := $(filter %.asm,$(my_src_files)) ifneq ($(strip $(asm_sources_asm)),) asm_objects_asm := $(addprefix $(intermediates)/,$(asm_sources_asm:.asm=.o)) $(asm_objects_asm): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.asm \ $(my_additional_dependencies) $(YASM) $(transform-asm-to-o) $(call track-src-file-obj,$(asm_sources_asm),$(asm_objects_asm)) asm_objects += $(asm_objects_asm) endif ################################################################### ## Convert to sanitized names where they exist. ## These lists come from sanitizerStaticLibsMap; see ## build/soong/cc/sanitize.go ## ## $(1): list of static dependencies ## $(2): name of sanitizer (e.g. cfi, hwasan) ################################################################## define use_soong_sanitized_static_libraries $(foreach lib,$(1),$(if $(filter $(lib),\ $(SOONG_$(2)_$(my_image_variant)_$(my_arch)_STATIC_LIBRARIES)),\ $(lib).$(2),$(lib))) endef ################################################################### ## When compiling a CFI enabled target, use the .cfi variant of any ## static dependencies (where they exist). ################################################################## ifneq ($(filter cfi,$(my_sanitize)),) my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_whole_static_libraries),cfi) my_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_static_libraries),cfi) endif ################################################################### ## When compiling a hwasan enabled target, use the .hwasan variant ## of any static dependencies (where they exist). ################################################################## ifneq ($(filter hwaddress,$(my_sanitize)),) my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_whole_static_libraries),hwasan) my_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_static_libraries),hwasan) endif ################################################################### ## When compiling a memtag_stack enabled target, use the .memtag_stack variant ## of any static dependencies (where they exist). ################################################################## ifneq ($(filter memtag_stack,$(my_sanitize)),) my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_whole_static_libraries),memtag_stack) my_static_libraries := $(call use_soong_sanitized_static_libraries,\ $(my_static_libraries),memtag_stack) endif ################################################################### ## When compiling against API imported module, use API import stub ## libraries. ################################################################## apiimport_postfix := .apiimport ifneq ($(call module-in-vendor-or-product),) ifeq ($(LOCAL_IN_PRODUCT),true) apiimport_postfix := .apiimport.product else apiimport_postfix := .apiimport.vendor endif endif my_shared_libraries := $(foreach l,$(my_shared_libraries), \ $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries), \ $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) my_header_libraries := $(foreach l,$(my_header_libraries), \ $(if $(filter $(l), $(API_IMPORTED_HEADER_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) ########################################################### ## When compiling against the VNDK, use LL-NDK libraries ########################################################### ifneq ($(call module-in-vendor-or-product),) ##################################################### ## Soong modules may be built three times, once for ## /system, once for /vendor and once for /product. ## If we're using the VNDK, switch all soong ## libraries over to the /vendor or /product variant. ##################################################### ifeq ($(LOCAL_IN_PRODUCT),true) my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\ $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l))) my_static_libraries := $(foreach l,$(my_static_libraries),\ $(if $(SPLIT_PRODUCT.STATIC_LIBRARIES.$(l)),$(l).product,$(l))) my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) my_header_libraries := $(foreach l,$(my_header_libraries),\ $(if $(SPLIT_PRODUCT.HEADER_LIBRARIES.$(l)),$(l).product,$(l))) else my_whole_static_libraries := $(foreach l,$(my_whole_static_libraries),\ $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l))) my_static_libraries := $(foreach l,$(my_static_libraries),\ $(if $(SPLIT_VENDOR.STATIC_LIBRARIES.$(l)),$(l).vendor,$(l))) my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) my_header_libraries := $(foreach l,$(my_header_libraries),\ $(if $(SPLIT_VENDOR.HEADER_LIBRARIES.$(l)),$(l).vendor,$(l))) endif endif # Platform can use vendor public libraries. If a required shared lib is one of # the vendor public libraries, the lib is switched to the stub version of the lib. ifeq ($(call module-in-vendor-or-product),) my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(filter $(l),$(VENDOR_PUBLIC_LIBRARIES)),$(l).vendorpublic,$(l))) endif ########################################################### ## When compiling against the NDK, use SDK variants of Soong libraries ########################################################### ifneq ($(LOCAL_SDK_VERSION),) my_whole_static_libraries := $(call use_soong_sdk_libraries,$(my_whole_static_libraries)) my_static_libraries := $(call use_soong_sdk_libraries,$(my_static_libraries)) my_shared_libraries := $(call use_soong_sdk_libraries,$(my_shared_libraries)) my_system_shared_libraries := $(call use_soong_sdk_libraries,$(my_system_shared_libraries)) my_header_libraries := $(call use_soong_sdk_libraries,$(my_header_libraries)) endif ########################################################## ## Set up installed module dependency ## We cannot compute the full path of the LOCAL_SHARED_LIBRARIES for ## they may cusomize their install path with LOCAL_MODULE_PATH ########################################################## # Get the list of INSTALLED libraries as module names. ifneq ($(LOCAL_SDK_VERSION),) installed_shared_library_module_names := \ $(my_shared_libraries) else installed_shared_library_module_names := \ $(my_shared_libraries) $(my_system_shared_libraries) endif # The real dependency will be added after all Android.mks are loaded and the install paths # of the shared libraries are determined. ifdef LOCAL_INSTALLED_MODULE ifdef installed_shared_library_module_names $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(installed_shared_library_module_names)) endif endif #################################################### ## Verify that NDK-built libraries only link against ## other NDK-built libraries #################################################### include $(BUILD_SYSTEM)/allowed_ndk_types.mk ifdef LOCAL_SDK_VERSION my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type) my_warn_types := $(my_warn_ndk_types) my_allowed_types := $(my_allowed_ndk_types) else ifeq ($(call module-in-vendor-or-product),true) _name := $(patsubst %.vendor,%,$(LOCAL_MODULE)) _name := $(patsubst %.product,%,$(LOCAL_MODULE)) ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),) ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),) my_link_type := native:vndk else my_link_type := native:vndk_private endif my_warn_types := my_allowed_types := native:vndk native:vndk_private else ifeq ($(LOCAL_IN_PRODUCT),true) # Modules installed to /product cannot directly depend on modules marked # with vendor_available: false my_link_type := native:product my_warn_types := my_allowed_types := native:product native:vndk native:platform_vndk else # Modules installed to /vendor cannot directly depend on modules marked # with vendor_available: false my_link_type := native:vendor my_warn_types := my_allowed_types := native:vendor native:vndk native:platform_vndk endif else ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(call get_non_asan_path,$(LOCAL_MODULE_PATH))),) my_link_type := native:recovery my_warn_types := # TODO(b/113303515) remove native:platform and my_allowed_ndk_types my_allowed_types := native:recovery native:platform native:platform_vndk $(my_allowed_ndk_types) else my_link_type := native:platform my_warn_types := $(my_warn_ndk_types) my_allowed_types := $(my_allowed_ndk_types) native:platform native:platform_vndk endif ALL_MODULES.$(my_register_name).WHOLE_STATIC_LIBS := $(my_whole_static_libraries) my_link_deps := $(addprefix STATIC_LIBRARIES:,$(my_whole_static_libraries) $(my_static_libraries)) ifneq ($(filter-out STATIC_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),) my_link_deps += $(addprefix SHARED_LIBRARIES:,$(my_shared_libraries)) endif my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := include $(BUILD_SYSTEM)/link_type.mk ########################################################### ## Common object handling. ########################################################### my_unused_src_files := $(filter-out $(logtags_sources) $(my_tracked_src_files),$(my_src_files) $(my_gen_src_files)) ifneq ($(my_unused_src_files),) $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unused_src_files)) endif # some rules depend on asm_objects being first. If your code depends on # being first, it's reasonable to require it to be assembly normal_objects := \ $(asm_objects) \ $(cpp_objects) \ $(gen_cpp_objects) \ $(gen_asm_objects) \ $(c_objects) \ $(gen_c_objects) \ $(objc_objects) \ $(objcpp_objects) new_order_normal_objects := $(foreach f,$(my_src_files),$(my_src_file_obj_$(f))) new_order_normal_objects += $(foreach f,$(my_gen_src_files),$(my_src_file_obj_$(f))) ifneq ($(sort $(normal_objects)),$(sort $(new_order_normal_objects))) $(warning $(LOCAL_MODULE_MAKEFILE) Internal build system warning: New object list does not match old) $(info Only in old: $(filter-out $(new_order_normal_objects),$(sort $(normal_objects)))) $(info Only in new: $(filter-out $(normal_objects),$(sort $(new_order_normal_objects)))) endif ifeq ($(BINARY_OBJECTS_ORDER),soong) normal_objects := $(new_order_normal_objects) endif normal_objects += $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES)) all_objects := $(normal_objects) $(gen_o_objects) LOCAL_INTERMEDIATE_TARGETS += $(all_objects) # Cleanup file tracking $(foreach f,$(my_tracked_gen_files),$(eval my_src_file_gen_$(s):=)) my_tracked_gen_files := $(foreach f,$(my_tracked_src_files),$(eval my_src_file_obj_$(s):=)) my_tracked_src_files := my_c_includes += $(TOPDIR)$(LOCAL_PATH) $(intermediates) $(generated_sources_dir) my_c_includes := $(foreach inc,$(my_c_includes),$(call clean-path,$(inc))) my_outside_includes := $(filter-out $(OUT_DIR)/%,$(filter /%,$(my_c_includes)) $(filter ../%,$(my_c_includes))) ifneq ($(my_outside_includes),) ifeq ($(BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS),true) $(call pretty-warning,C_INCLUDES must be under the source or output directories: $(my_outside_includes)) else $(call pretty-error,C_INCLUDES must be under the source or output directories: $(my_outside_includes)) endif endif # all_objects includes gen_o_objects which were part of LOCAL_GENERATED_SOURCES; # use normal_objects here to avoid creating circular dependencies. This assumes # that custom build rules which generate .o files don't consume other generated # sources as input (or if they do they take care of that dependency themselves). $(normal_objects) : | $(my_generated_sources) ALL_C_CPP_ETC_OBJECTS += $(all_objects) ########################################################### # Standard library handling. ########################################################### ########################################################### # The list of libraries that this module will link against are in # these variables. Each is a list of bare module names like "libc libm". # # LOCAL_SHARED_LIBRARIES # LOCAL_STATIC_LIBRARIES # LOCAL_WHOLE_STATIC_LIBRARIES # # We need to convert the bare names into the dependencies that # we'll use for LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE. # LOCAL_BUILT_MODULE should depend on the BUILT versions of the # libraries, so that simply building this module doesn't force # an install of a library. Similarly, LOCAL_INSTALLED_MODULE # should depend on the INSTALLED versions of the libraries so # that they get installed when this module does. ########################################################### # NOTE: # WHOLE_STATIC_LIBRARIES are libraries that are pulled into the # module without leaving anything out, which is useful for turning # a collection of .a files into a .so file. Linking against a # normal STATIC_LIBRARY will only pull in code/symbols that are # referenced by the module. (see gcc/ld's --whole-archive option) ########################################################### # Get the list of BUILT libraries, which are under # various intermediates directories. so_suffix := $($(my_prefix)SHLIB_SUFFIX) a_suffix := $($(my_prefix)STATIC_LIB_SUFFIX) ifneq ($(LOCAL_SDK_VERSION),) built_shared_libraries := \ $(foreach lib,$(my_shared_libraries), \ $(call intermediates-dir-for, \ SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix)) built_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries)) # Add the NDK libraries to the built module dependency my_system_shared_libraries_fullpath := \ $(my_ndk_stl_shared_lib_fullpath) \ $(addprefix $(my_ndk_sysroot_lib)/, \ $(addsuffix $(so_suffix), $(my_system_shared_libraries))) # We need to preserve the ordering of LOCAL_SHARED_LIBRARIES regardless of # whether the libs are generated or prebuilt, so we simply can't split into two # lists and use addprefix. my_ndk_shared_libraries_fullpath := \ $(foreach _lib,$(my_ndk_shared_libraries),\ $(if $(filter $(NDK_KNOWN_LIBS),$(_lib)),\ $(my_ndk_sysroot_lib)/$(_lib)$(so_suffix))) built_shared_libraries += \ $(my_ndk_shared_libraries_fullpath) \ $(my_system_shared_libraries_fullpath) \ built_shared_library_deps += \ $(my_ndk_shared_libraries_fullpath) \ $(my_system_shared_libraries_fullpath) \ else built_shared_libraries := \ $(foreach lib,$(installed_shared_library_module_names), \ $(call intermediates-dir-for, \ SHARED_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(so_suffix)) built_shared_library_deps := $(addsuffix .toc, $(built_shared_libraries)) my_system_shared_libraries_fullpath := endif built_static_libraries := \ $(foreach lib,$(my_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix)) ifdef LOCAL_SDK_VERSION built_static_libraries += $(my_ndk_stl_static_lib) endif built_whole_libraries := \ $(foreach lib,$(my_whole_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix)) # Default is -fno-rtti. ifeq ($(strip $(LOCAL_RTTI_FLAG)),) LOCAL_RTTI_FLAG := -fno-rtti endif ########################################################### # Rule-specific variable definitions ########################################################### my_cflags += $(LOCAL_CLANG_CFLAGS) my_conlyflags += $(LOCAL_CLANG_CONLYFLAGS) my_cppflags += $(LOCAL_CLANG_CPPFLAGS) my_asflags += $(LOCAL_CLANG_ASFLAGS) my_ldflags += $(LOCAL_CLANG_LDFLAGS) my_cflags += $(LOCAL_CLANG_CFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CFLAGS_$(my_32_64_bit_suffix)) my_conlyflags += $(LOCAL_CLANG_CONLYFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CONLYFLAGS_$(my_32_64_bit_suffix)) my_cppflags += $(LOCAL_CLANG_CPPFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_CPPFLAGS_$(my_32_64_bit_suffix)) my_ldflags += $(LOCAL_CLANG_LDFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_LDFLAGS_$(my_32_64_bit_suffix)) my_asflags += $(LOCAL_CLANG_ASFLAGS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_CLANG_ASFLAGS_$(my_32_64_bit_suffix)) my_cflags := $(call convert-to-clang-flags,$(my_cflags)) my_cppflags := $(call convert-to-clang-flags,$(my_cppflags)) my_asflags := $(call convert-to-clang-flags,$(my_asflags)) my_ldflags := $(call convert-to-clang-flags,$(my_ldflags)) # No one should ever use this flag. On GCC it's mere presence will disable all # warnings, even those that are specified after it (contrary to typical warning # flag behavior). This circumvents CFLAGS_NO_OVERRIDE from forcibly enabling the # warnings that are *always* bugs. my_illegal_flags := -w my_cflags := $(filter-out $(my_illegal_flags),$(my_cflags)) my_cppflags := $(filter-out $(my_illegal_flags),$(my_cppflags)) my_conlyflags := $(filter-out $(my_illegal_flags),$(my_conlyflags)) # We can enforce some rules more strictly in the code we own. my_strict # indicates if this is code that we can be stricter with. If we have rules that # we want to apply to *our* code (but maybe can't for vendor/device specific # things), we could extend this to be a ternary value. my_strict := true ifneq ($(filter external/%,$(LOCAL_PATH)),) my_strict := false endif # Can be used to make some annotations stricter for code we can fix (such as # when we mark functions as deprecated). ifeq ($(my_strict),true) my_cflags += -DANDROID_STRICT endif # Check if -Werror or -Wno-error is used in C compiler flags. # Header libraries do not need cflags. my_all_cflags := $(my_cflags) $(my_cppflags) $(my_cflags_no_override) ifneq (HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)) # Prebuilt modules do not need cflags. ifeq (,$(LOCAL_PREBUILT_MODULE_FILE)) # Issue warning if -Wno-error is used. ifneq (,$(filter -Wno-error,$(my_all_cflags))) $(eval MODULES_USING_WNO_ERROR := $(MODULES_USING_WNO_ERROR) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE)) else # Issue warning if -Werror is not used. Add it. ifeq (,$(filter -Werror,$(my_all_cflags))) # Add -Wall -Werror unless the project is in the WARNING_ALLOWED project list. ifeq (,$(strip $(call find_warning_allowed_projects,$(LOCAL_PATH)))) my_cflags := -Wall -Werror $(my_cflags) else $(eval MODULES_WARNINGS_ALLOWED := $(MODULES_USING_WNO_ERROR) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE)) my_cflags := -Wall $(my_cflags) endif endif endif endif endif ifneq (,$(filter -Weverything,$(my_all_cflags))) ifeq (,$(ANDROID_TEMPORARILY_ALLOW_WEVERYTHING)) $(call pretty-error, -Weverything is not allowed in Android.mk files.\ Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.) endif endif ifneq ($(my_tidy_checks),) tidy_only: $(cpp_objects) $(c_objects) $(gen_c_objects) $(gen_cpp_objects) # Add dependency of clang-tidy and clang-tidy.sh $(cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) $(c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) $(gen_cpp_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) $(gen_c_objects): $(intermediates)/%.o: $(PATH_TO_CLANG_TIDY) endif # Move -l* entries from ldflags to ldlibs, and everything else to ldflags my_ldlib_flags := $(my_ldflags) $(my_ldlibs) my_ldlibs := $(filter -l%,$(my_ldlib_flags)) my_ldflags := $(filter-out -l%,$(my_ldlib_flags)) # One last verification check for ldlibs my_allowed_ldlibs := ifndef LOCAL_IS_HOST_MODULE ifneq ($(LOCAL_SDK_VERSION),) my_allowed_ldlibs := $(NDK_KNOWN_LIBS:lib%=-l%) endif else my_allowed_ldlibs := $($(my_prefix)AVAILABLE_LIBRARIES) endif my_bad_ldlibs := $(filter-out $(my_allowed_ldlibs),$(my_ldlibs)) ifneq ($(my_bad_ldlibs),) $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Bad LOCAL_LDLIBS entries: $(my_bad_ldlibs)) endif # my_cxx_ldlibs may contain linker flags need to wrap certain libraries # (start-group/end-group), so append after the check above. my_ldlibs += $(my_cxx_ldlibs) ########################################################### ## Define PRIVATE_ variables from global vars ########################################################### ifndef LOCAL_IS_HOST_MODULE my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS) my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags) my_target_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags) ifeq ($(my_use_clang_lld),true) my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS) include $(BUILD_SYSTEM)/pack_dyn_relocs_setup.mk ifeq ($(my_pack_module_relocations),true) my_target_global_ldflags += -Wl,--pack-dyn-relocs=android+relr -Wl,--use-android-relr-tags else my_target_global_ldflags += -Wl,--pack-dyn-relocs=none endif else my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS) endif # my_use_clang_lld ifeq ($(call module-in-vendor-or-product),true) my_target_global_c_includes := my_target_global_c_system_includes := $(TARGET_OUT_HEADERS) my_target_global_cflags += -nostdlibinc else ifdef LOCAL_SDK_VERSION my_target_global_c_includes := my_target_global_c_system_includes := $(my_ndk_stl_include_path) my_target_global_cflags += --sysroot $(my_ndk_sysroot) else my_target_global_c_includes := $(SRC_HEADERS) \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) my_target_global_cflags += -nostdlibinc endif my_target_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)TRIPLE) ifndef LOCAL_IS_HOST_MODULE my_target_triple_flag := -target $(my_target_triple)$(my_api_level) else my_target_triple_flag := -target $(my_target_triple) endif my_asflags += $(my_target_triple_flag) my_cflags += $(my_target_triple_flag) my_ldflags += $(my_target_triple_flag) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_target_global_c_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_target_global_c_system_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CFLAGS := $(my_target_global_cflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CONLYFLAGS := $(my_target_global_conlyflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CPPFLAGS := $(my_target_global_cppflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_LDFLAGS := $(my_target_global_ldflags) else # LOCAL_IS_HOST_MODULE my_host_global_c_includes := $(SRC_HEADERS) \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) my_host_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) my_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS) my_host_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags) my_host_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags) ifeq ($(my_use_clang_lld),true) my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LLDFLAGS) else my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS) endif # my_use_clang_lld $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_host_global_c_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_host_global_c_system_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CFLAGS := $(my_host_global_cflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CONLYFLAGS := $(my_host_global_conlyflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CPPFLAGS := $(my_host_global_cppflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_LDFLAGS := $(my_host_global_ldflags) endif # LOCAL_IS_HOST_MODULE # To enable coverage for a given module, set LOCAL_NATIVE_COVERAGE=true and # build with NATIVE_COVERAGE=true in your enviornment. ifeq ($(NATIVE_COVERAGE),true) ifeq ($(my_native_coverage),true) # Note that clang coverage doesn't play nicely with acov out of the box. # Clang apparently generates .gcno files that aren't compatible with # gcov-4.8. This can be solved by installing gcc-4.6 and invoking lcov # with `--gcov-tool /usr/bin/gcov-4.6`. # # http://stackoverflow.com/questions/17758126/clang-code-coverage-invalid-output my_cflags += --coverage -O0 my_ldflags += --coverage endif my_coverage_lib := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_COVERAGE_LIB := $(my_coverage_lib) $(LOCAL_INTERMEDIATE_TARGETS): $(my_coverage_lib) endif #################################################### ## Import includes #################################################### imported_includes := ifeq (true,$(call module-in-vendor-or-product)) imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)) else # everything else should manually specify headers endif imported_includes := $(strip \ $(imported_includes) \ $(foreach l, $(installed_shared_library_module_names), \ $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \ $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \ $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \ $(foreach l, $(my_header_libraries), \ $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) $(foreach dep,$(imported_includes),\ $(eval EXPORTS.$$(dep).USERS := $$(EXPORTS.$$(dep).USERS) $$(all_objects))) ########################################################### ## Define PRIVATE_ variables used by multiple module types ########################################################### $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_NO_DEFAULT_COMPILER_FLAGS := \ $(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)) ifeq ($(strip $(WITH_STATIC_ANALYZER)),) LOCAL_NO_STATIC_ANALYZER := true endif ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),) my_syntax_arch := host else my_syntax_arch := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) endif ifeq ($(strip $(my_cc)),) my_cc := $(my_cc_wrapper) $(CLANG) endif SYNTAX_TOOLS_PREFIX := \ $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/libexec ifneq ($(LOCAL_NO_STATIC_ANALYZER),true) my_cc := CCC_CC=$(CLANG) CLANG=$(CLANG) \ $(SYNTAX_TOOLS_PREFIX)/ccc-analyzer endif $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CC := $(my_cc) ifeq ($(strip $(my_cxx)),) my_cxx := $(my_cxx_wrapper) $(CLANG_CXX) endif ifeq ($(strip $(my_cxx_link)),) my_cxx_link := $(CLANG_CXX) endif ifneq ($(LOCAL_NO_STATIC_ANALYZER),true) my_cxx := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \ $(SYNTAX_TOOLS_PREFIX)/c++-analyzer my_cxx_link := CCC_CXX=$(CLANG_CXX) CLANG_CXX=$(CLANG_CXX) \ $(SYNTAX_TOOLS_PREFIX)/c++-analyzer endif $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LINKER := $(my_linker) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX := $(my_cxx) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX_LINK := $(my_cxx_link) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_YACCFLAGS := $(LOCAL_YACCFLAGS) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASFLAGS := $(my_asflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CONLYFLAGS := $(my_conlyflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS := $(my_cflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS := $(my_cppflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS_NO_OVERRIDE := $(my_cflags_no_override) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS_NO_OVERRIDE := $(my_cppflags_no_override) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RTTI_FLAG := $(LOCAL_RTTI_FLAG) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEBUG_CFLAGS := $(debug_cflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_C_INCLUDES := $(my_c_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORTED_INCLUDES := $(imported_includes) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(my_ldflags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(my_ldlibs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_CHECKS := $(my_tidy_checks) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_FLAGS := $(my_tidy_flags) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ARFLAGS := $(my_arflags) # this is really the way to get the files onto the command line instead # of using $^, because then LOCAL_ADDITIONAL_DEPENDENCIES doesn't work $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_SHARED_LIBRARIES := $(built_shared_libraries) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_STATIC_LIBRARIES := $(built_static_libraries) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(built_whole_libraries) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_OBJECTS := $(strip $(all_objects)) ########################################################### # Define library dependencies. ########################################################### # all_libraries is used for the dependencies on LOCAL_BUILT_MODULE. all_libraries := \ $(built_shared_library_deps) \ $(my_system_shared_libraries_fullpath) \ $(built_static_libraries) \ $(built_whole_libraries) ########################################################### # Export includes ########################################################### # Headers exported by whole static libraries are also exported by this library. export_include_deps := $(strip \ $(foreach l,$(my_whole_static_libraries), \ $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) # Re-export requested headers from shared libraries. export_include_deps += $(strip \ $(foreach l,$(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS), \ $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) # Re-export requested headers from static libraries. export_include_deps += $(strip \ $(foreach l,$(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS), \ $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) # Re-export requested headers from header libraries. export_include_deps += $(strip \ $(foreach l,$(LOCAL_EXPORT_HEADER_LIBRARY_HEADERS), \ $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross)))) ifneq ($(strip $(my_export_c_include_dirs)$(export_include_deps)),) EXPORTS_LIST += $(intermediates) EXPORTS.$(intermediates).FLAGS := $(foreach d,$(my_export_c_include_dirs),-I $(call clean-path,$(d))) EXPORTS.$(intermediates).REEXPORT := $(export_include_deps) EXPORTS.$(intermediates).DEPS := $(my_export_c_include_deps) $(my_generated_sources) $(LOCAL_EXPORT_C_INCLUDE_DEPS) endif ifneq (,$(filter-out $(LOCAL_PATH)/%,$(my_export_c_include_dirs))) my_soong_problems += non_local__export_c_include_dirs endif SOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \ $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems) SOONG_CONV.$(LOCAL_MODULE).DEPS := \ $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \ $(filter-out $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY),\ $(my_static_libraries) \ $(my_whole_static_libraries) \ $(my_shared_libraries) \ $(my_system_shared_libraries)) SOONG_CONV.$(LOCAL_MODULE).TYPE := native SOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \ $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE) SOONG_CONV.$(LOCAL_MODULE).INSTALLED:= \ $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE) SOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE) ########################################################### # Coverage packaging. ########################################################### ifeq ($(my_native_coverage),true) my_gcno_objects := \ $(cpp_objects) \ $(gen_cpp_objects) \ $(c_objects) \ $(gen_c_objects) \ $(objc_objects) \ $(objcpp_objects) LOCAL_GCNO_FILES := $(patsubst %.o,%.gcno,$(my_gcno_objects)) $(foreach f,$(my_gcno_objects),$(eval $(call gcno-touch-rule,$(f),$(f:.o=.gcno)))) endif ================================================ FILE: core/board_config.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ############################################################### # This file includes BoardConfig.mk for the device being built, # and checks the variable defined therein. # ############################################################### _board_strip_readonly_list := _board_strip_readonly_list += BOARD_BOOTLOADER_IN_UPDATE_PACKAGE _board_strip_readonly_list += BOARD_EGL_CFG _board_strip_readonly_list += BOARD_HAVE_BLUETOOTH _board_strip_readonly_list += BOARD_INSTALLER_CMDLINE _board_strip_readonly_list += BOARD_KERNEL_CMDLINE _board_strip_readonly_list += BOARD_BOOT_HEADER_VERSION _board_strip_readonly_list += BOARD_BOOTCONFIG _board_strip_readonly_list += BOARD_BOOTCONFIG_FILE _board_strip_readonly_list += BOARD_KERNEL_BASE _board_strip_readonly_list += BOARD_USES_GENERIC_AUDIO _board_strip_readonly_list += BOARD_USES_RECOVERY_AS_BOOT _board_strip_readonly_list += BOARD_VENDOR_USE_AKMD _board_strip_readonly_list += BOARD_WPA_SUPPLICANT_DRIVER _board_strip_readonly_list += BOARD_WLAN_DEVICE _board_strip_readonly_list += TARGET_BOARD_PLATFORM _board_strip_readonly_list += TARGET_BOARD_PLATFORM_GPU _board_strip_readonly_list += TARGET_BOOTLOADER_BOARD_NAME _board_strip_readonly_list += TARGET_FS_CONFIG_GEN _board_strip_readonly_list += TARGET_NO_BOOTLOADER _board_strip_readonly_list += TARGET_NO_KERNEL _board_strip_readonly_list += TARGET_NO_RECOVERY _board_strip_readonly_list += TARGET_NO_RADIOIMAGE _board_strip_readonly_list += TARGET_HARDWARE_3D _board_strip_readonly_list += WITH_DEXPREOPT # Arch variables _board_strip_readonly_list += TARGET_ARCH _board_strip_readonly_list += TARGET_ARCH_VARIANT _board_strip_readonly_list += TARGET_CPU_ABI _board_strip_readonly_list += TARGET_CPU_ABI2 _board_strip_readonly_list += TARGET_CPU_VARIANT _board_strip_readonly_list += TARGET_CPU_VARIANT_RUNTIME _board_strip_readonly_list += TARGET_2ND_ARCH _board_strip_readonly_list += TARGET_2ND_ARCH_VARIANT _board_strip_readonly_list += TARGET_2ND_CPU_ABI _board_strip_readonly_list += TARGET_2ND_CPU_ABI2 _board_strip_readonly_list += TARGET_2ND_CPU_VARIANT _board_strip_readonly_list += TARGET_2ND_CPU_VARIANT_RUNTIME # TARGET_ARCH_SUITE is an alternative arch configuration to TARGET_ARCH (and related variables), # that can be used for soong-only builds to build for several architectures at once. # Allowed values currently are "ndk" and "mainline_sdk". _board_strip_readonly_list += TARGET_ARCH_SUITE # File system variables _board_strip_readonly_list += BOARD_FLASH_BLOCK_SIZE _board_strip_readonly_list += BOARD_BOOTIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_RECOVERYIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_USERDATAIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_CACHEIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_ODMIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE _board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE _board_strip_readonly_list += BOARD_PVMFWIMAGE_PARTITION_SIZE # Logical partitions related variables. _board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE _board_strip_readonly_list += BOARD_SUPER_PARTITION_SIZE _board_strip_readonly_list += BOARD_SUPER_PARTITION_GROUPS # Kernel related variables _board_strip_readonly_list += BOARD_KERNEL_BINARIES _board_strip_readonly_list += BOARD_KERNEL_MODULE_INTERFACE_VERSIONS # Variables related to generic kernel image (GKI) and generic boot image # - BOARD_USES_GENERIC_KERNEL_IMAGE is the global variable that defines if the # board uses GKI and generic boot image. # Update mechanism of the boot image is not enforced by this variable. # - BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE controls whether the recovery image # contains a kernel or not. # - BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT controls whether ramdisk # recovery resources are built to vendor_boot. # - BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT controls whether recovery # resources are built as a standalone recovery ramdisk in vendor_boot. # - BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT controls whether GSI AVB keys are # built to vendor_boot. # - BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES controls whether boot images in $OUT are added # to target files package directly. _board_strip_readonly_list += BOARD_USES_GENERIC_KERNEL_IMAGE _board_strip_readonly_list += BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE _board_strip_readonly_list += BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT _board_strip_readonly_list += BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT _board_strip_readonly_list += BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT _board_strip_readonly_list += BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES # Prebuilt image variables _board_strip_readonly_list += BOARD_PREBUILT_INIT_BOOT_IMAGE # Defines the list of logical vendor ramdisk names to build or include in vendor_boot. _board_strip_readonly_list += BOARD_VENDOR_RAMDISK_FRAGMENTS # These are all variables used to build $(INSTALLED_MISC_INFO_TARGET) # in build/make/core/Makefile. Their values get used in command line # arguments, so they have to be stripped to make the ninja files stable. _board_strip_list := _board_strip_list += BOARD_DTBOIMG_PARTITION_SIZE _board_strip_list += BOARD_AVB_DTBO_KEY_PATH _board_strip_list += BOARD_AVB_DTBO_ALGORITHM _board_strip_list += BOARD_AVB_DTBO_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_AVB_PVMFW_KEY_PATH _board_strip_list += BOARD_AVB_PVMFW_ALGORITHM _board_strip_list += BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST _board_strip_list += BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_AVB_VBMETA_VENDOR_ALGORITHM _board_strip_list += BOARD_AVB_VBMETA_VENDOR_KEY_PATH _board_strip_list += BOARD_AVB_VBMETA_VENDOR _board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_AVB_VBMETA_SYSTEM_ALGORITHM _board_strip_list += BOARD_AVB_VBMETA_SYSTEM_KEY_PATH _board_strip_list += BOARD_AVB_VBMETA_SYSTEM _board_strip_list += BOARD_AVB_RECOVERY_KEY_PATH _board_strip_list += BOARD_AVB_RECOVERY_ALGORITHM _board_strip_list += BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_AVB_VENDOR_BOOT_KEY_PATH _board_strip_list += BOARD_AVB_VENDOR_BOOT_ALGORITHM _board_strip_list += BOARD_AVB_VENDOR_BOOT_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_KEY_PATH _board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ALGORITHM _board_strip_list += BOARD_AVB_VENDOR_KERNEL_BOOT_ROLLBACK_INDEX_LOCATION _board_strip_list += BOARD_MKBOOTIMG_ARGS _board_strip_list += BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE _board_strip_list += BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE _board_strip_list += ODM_MANIFEST_SKUS _build_broken_var_list := \ BUILD_BROKEN_CLANG_PROPERTY \ BUILD_BROKEN_CLANG_ASFLAGS \ BUILD_BROKEN_CLANG_CFLAGS \ BUILD_BROKEN_DEPFILE \ BUILD_BROKEN_DUP_RULES \ BUILD_BROKEN_DUP_SYSPROP \ BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES \ BUILD_BROKEN_ENFORCE_SYSPROP_OWNER \ BUILD_BROKEN_INPUT_DIR_MODULES \ BUILD_BROKEN_MISSING_REQUIRED_MODULES \ BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS \ BUILD_BROKEN_PREBUILT_ELF_FILES \ BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW \ BUILD_BROKEN_USES_NETWORK \ BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE \ BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES \ BUILD_BROKEN_INCORRECT_PARTITION_IMAGES \ BUILD_BROKEN_GENRULE_SANDBOXING \ BUILD_BROKEN_DONT_CHECK_SYSTEMSDK \ _build_broken_var_list += \ $(foreach m,$(AVAILABLE_BUILD_MODULE_TYPES) \ $(DEFAULT_WARNING_BUILD_MODULE_TYPES) \ $(DEFAULT_ERROR_BUILD_MODULE_TYPES), \ BUILD_BROKEN_USES_$(m)) _board_true_false_vars := $(_build_broken_var_list) _board_strip_readonly_list += $(_build_broken_var_list) \ BUILD_BROKEN_NINJA_USES_ENV_VARS # Conditional to building on linux, as dex2oat currently does not work on darwin. ifeq ($(HOST_OS),linux) WITH_DEXPREOPT ?= true endif # ############################################################### # Broken build defaults # ############################################################### $(foreach v,$(_build_broken_var_list),$(eval $(v) :=)) BUILD_BROKEN_NINJA_USES_ENV_VARS := # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE) # or under vendor/*/$(TARGET_DEVICE). Search in both places, but # make sure only one exists. # Real boards should always be associated with an OEM vendor. ifdef TARGET_DEVICE_DIR ifneq ($(origin TARGET_DEVICE_DIR),command line) $(error TARGET_DEVICE_DIR may not be set manually) endif board_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk else board_config_mk := \ $(strip $(sort $(wildcard \ $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ device/generic/goldfish/board/$(TARGET_DEVICE)/BoardConfig.mk \ device/google/cuttlefish/board/$(TARGET_DEVICE)/BoardConfig.mk \ vendor/google/products/cuttlefish/pixel_watch/board/$(TARGET_DEVICE)/BoardConfig.mk \ $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ ))) ifeq ($(board_config_mk),) $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE)) endif ifneq ($(words $(board_config_mk)),1) $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk)) endif TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))) .KATI_READONLY := TARGET_DEVICE_DIR endif $(call dump-phase-start,BOARD,,,, build/make/core/board_config.mk) ifndef RBC_PRODUCT_CONFIG include $(board_config_mk) else $(shell mkdir -p $(OUT_DIR)/rbc) $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_board_config.mk) $(shell $(OUT_DIR)/mk2rbc \ --mode=write -r --outdir $(OUT_DIR)/rbc \ --boardlauncher=$(OUT_DIR)/rbc/boardlauncher.rbc \ --input_variables=$(OUT_DIR)/rbc/make_vars_pre_board_config.mk \ --makefile_list=$(OUT_DIR)/.module_paths/configuration.list \ $(board_config_mk)) ifneq ($(.SHELLSTATUS),0) $(error board configuration converter failed: $(.SHELLSTATUS)) endif $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_board_config_results.mk \ $(OUT_DIR)/rbcrun --mode=rbc $(OUT_DIR)/rbc/boardlauncher.rbc) ifneq ($(.SHELLSTATUS),0) $(error board configuration runner failed: $(.SHELLSTATUS)) endif include $(OUT_DIR)/rbc/rbc_board_config_results.mk endif $(call dump-phase-end, build/make/core/board_config.mk) ifneq (,$(and $(TARGET_ARCH),$(TARGET_ARCH_SUITE))) $(error $(board_config_mk) erroneously sets both TARGET_ARCH and TARGET_ARCH_SUITE) endif ifeq ($(TARGET_ARCH)$(TARGET_ARCH_SUITE),) $(error Target architectures not defined by board config: $(board_config_mk)) endif ifeq ($(TARGET_CPU_ABI)$(TARGET_ARCH_SUITE),) $(error TARGET_CPU_ABI not defined by board config: $(board_config_mk)) endif ifneq ($(MALLOC_IMPL),) $(warning *** Unsupported option MALLOC_IMPL defined by board config: $(board_config_mk).) $(error Use `MALLOC_LOW_MEMORY := true` to use low-memory allocator config) endif board_config_mk := # Clean up and verify BoardConfig variables $(foreach var,$(_board_strip_readonly_list),$(eval $(var) := $$(strip $$($(var))))) $(foreach var,$(_board_strip_list),$(eval $(var) := $$(strip $$($(var))))) $(foreach var,$(_board_true_false_vars), \ $(if $(filter-out true false,$($(var))), \ $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))"))) include $(BUILD_SYSTEM)/board_config_wifi.mk include $(BUILD_SYSTEM)/board_config_wpa_supplicant.mk # Set up soong config for "soong_config_value_variable". -include hardware/interfaces/configstore/1.1/default/surfaceflinger.mk -include vendor/google/build/soong/soong_config_namespace/camera.mk # Default *_CPU_VARIANT_RUNTIME to CPU_VARIANT if unspecified. TARGET_CPU_VARIANT_RUNTIME := $(or $(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_CPU_VARIANT)) TARGET_2ND_CPU_VARIANT_RUNTIME := $(or $(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_CPU_VARIANT)) ifdef TARGET_ARCH # The combo makefiles check and set defaults for various CPU configuration combo_target := TARGET_ combo_2nd_arch_prefix := include $(BUILD_SYSTEM)/combo/select.mk endif ifdef TARGET_2ND_ARCH combo_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/combo/select.mk endif .KATI_READONLY := $(_board_strip_readonly_list) INTERNAL_KERNEL_CMDLINE := $(BOARD_KERNEL_CMDLINE) ifneq (,$(BOARD_BOOTCONFIG)$(BOARD_BOOTCONFIG_FILE)) INTERNAL_KERNEL_CMDLINE += bootconfig INTERNAL_BOOTCONFIG := $(BOARD_BOOTCONFIG) INTERNAL_BOOTCONFIG_FILE := $(BOARD_BOOTCONFIG_FILE) endif ifneq ($(filter %64,$(TARGET_ARCH)),) TARGET_IS_64_BIT := true endif ifeq (,$(filter true,$(TARGET_SUPPORTS_32_BIT_APPS) $(TARGET_SUPPORTS_64_BIT_APPS))) TARGET_SUPPORTS_32_BIT_APPS := true endif # Quick check to warn about likely cryptic errors later in the build. ifeq ($(TARGET_IS_64_BIT),true) ifeq (,$(filter true false,$(TARGET_SUPPORTS_64_BIT_APPS))) $(error Building a 32-bit-app-only product on a 64-bit device. \ If this is intentional, set TARGET_SUPPORTS_64_BIT_APPS := false) endif endif # "ro.product.cpu.abilist32" and "ro.product.cpu.abilist64" are # comma separated lists of the 32 and 64 bit ABIs (in order of # preference) that the target supports. If TARGET_CPU_ABI_LIST_{32,64}_BIT # are defined by the board config, we use them. Else, we construct # these lists based on whether TARGET_IS_64_BIT is set. # # Note that this assumes that the 2ND_CPU_ABI for a 64 bit target # is always 32 bits. If this isn't the case, these variables should # be overriden in the board configuration. # # Similarly, TARGET_NATIVE_BRIDGE_2ND_ABI for a 64 bit target is always # 32 bits. Note that all CPU_ABIs are preferred over all NATIVE_BRIDGE_ABIs. _target_native_bridge_abi_list_32_bit := _target_native_bridge_abi_list_64_bit := ifeq (,$(TARGET_CPU_ABI_LIST_64_BIT)) ifeq (true|true,$(TARGET_IS_64_BIT)|$(TARGET_SUPPORTS_64_BIT_APPS)) TARGET_CPU_ABI_LIST_64_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2) _target_native_bridge_abi_list_64_bit := $(TARGET_NATIVE_BRIDGE_ABI) endif endif # "arm64-v8a-hwasan", the ABI for libraries compiled with HWASAN, is supported # in all builds with SANITIZE_TARGET=hwaddress. ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),) ifneq ($(filter arm64-v8a,$(TARGET_CPU_ABI_LIST_64_BIT)),) TARGET_CPU_ABI_LIST_64_BIT := arm64-v8a-hwasan $(TARGET_CPU_ABI_LIST_64_BIT) endif endif ifeq (,$(TARGET_CPU_ABI_LIST_32_BIT)) ifneq (true,$(TARGET_IS_64_BIT)) TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2) _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_ABI) else ifeq (true,$(TARGET_SUPPORTS_32_BIT_APPS)) # For a 64 bit target, assume that the 2ND_CPU_ABI # is a 32 bit ABI. TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2) _target_native_bridge_abi_list_32_bit := $(TARGET_NATIVE_BRIDGE_2ND_ABI) endif endif endif # "ro.product.cpu.abilist" is a comma separated list of ABIs (in order # of preference) that the target supports. If a TARGET_CPU_ABI_LIST # is specified by the board configuration, we use that. If not, we # build a list out of the TARGET_CPU_ABIs specified by the config. # Add NATIVE_BRIDGE_ABIs at the end to keep order of preference. ifeq (,$(TARGET_CPU_ABI_LIST)) TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_64_BIT) $(TARGET_CPU_ABI_LIST_32_BIT) \ $(_target_native_bridge_abi_list_64_bit) $(_target_native_bridge_abi_list_32_bit) endif # Add NATIVE_BRIDGE_ABIs at the end of 32 and 64 bit CPU_ABIs to keep order of preference. TARGET_CPU_ABI_LIST_32_BIT += $(_target_native_bridge_abi_list_32_bit) TARGET_CPU_ABI_LIST_64_BIT += $(_target_native_bridge_abi_list_64_bit) # Strip whitespace from the ABI list string. TARGET_CPU_ABI_LIST := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST))) TARGET_CPU_ABI_LIST_32_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_32_BIT))) TARGET_CPU_ABI_LIST_64_BIT := $(subst $(space),$(comma),$(strip $(TARGET_CPU_ABI_LIST_64_BIT))) # Check if config about image building is valid or not. define check_image_config $(eval _uc_name := $(call to-upper,$(1))) \ $(eval _lc_name := $(call to-lower,$(1))) \ $(if $(filter $(_lc_name),$(TARGET_COPY_OUT_$(_uc_name))), \ $(if $(BOARD_USES_$(_uc_name)IMAGE),, \ $(error If TARGET_COPY_OUT_$(_uc_name) is '$(_lc_name)', either BOARD_PREBUILT_$(_uc_name)IMAGE or BOARD_$(_uc_name)IMAGE_FILE_SYSTEM_TYPE must be set)), \ $(if $(BOARD_USES_$(_uc_name)IMAGE), \ $(error TARGET_COPY_OUT_$(_uc_name) must be set to '$(_lc_name)' to use a $(_lc_name) image))) \ $(eval _uc_name :=) \ $(eval _lc_name :=) endef ########################################### # Configure whether we're building the system image BUILDING_SYSTEM_IMAGE := true ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),) ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE ifndef BOARD_SYSTEMIMAGE_PARTITION_SIZE BUILDING_SYSTEM_IMAGE := endif endif else ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),false) BUILDING_SYSTEM_IMAGE := endif .KATI_READONLY := BUILDING_SYSTEM_IMAGE # Are we building a system_other image BUILDING_SYSTEM_OTHER_IMAGE := ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),) ifdef BUILDING_SYSTEM_IMAGE ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) BUILDING_SYSTEM_OTHER_IMAGE := true endif endif else ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),true) BUILDING_SYSTEM_OTHER_IMAGE := true ifndef BUILDING_SYSTEM_IMAGE $(error PRODUCT_BUILD_SYSTEM_OTHER_IMAGE = true requires building the system image) endif endif .KATI_READONLY := BUILDING_SYSTEM_OTHER_IMAGE # Are we building a cache image BUILDING_CACHE_IMAGE := ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),) ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE BUILDING_CACHE_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),true) BUILDING_CACHE_IMAGE := true ifndef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_CACHE_IMAGE set to true, but BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE not defined) endif endif .KATI_READONLY := BUILDING_CACHE_IMAGE # Are we building a boot image BUILDING_BOOT_IMAGE := ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) BUILDING_BOOT_IMAGE := else ifdef BOARD_PREBUILT_BOOTIMAGE BUILDING_BOOT_IMAGE := else ifdef BOARD_BOOTIMAGE_PARTITION_SIZE BUILDING_BOOT_IMAGE := true else ifneq (,$(foreach kernel,$(BOARD_KERNEL_BINARIES),$(BOARD_$(call to-upper,$(kernel))_BOOTIMAGE_PARTITION_SIZE))) BUILDING_BOOT_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),true) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(warning *** PRODUCT_BUILD_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT.) $(warning *** Skipping building boot image.) BUILDING_BOOT_IMAGE := else BUILDING_BOOT_IMAGE := true endif endif .KATI_READONLY := BUILDING_BOOT_IMAGE # Are we building an init boot image BUILDING_INIT_BOOT_IMAGE := ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) BUILDING_INIT_BOOT_IMAGE := else ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE BUILDING_INIT_BOOT_IMAGE := else ifdef BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE BUILDING_INIT_BOOT_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),true) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(error PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.) else BUILDING_INIT_BOOT_IMAGE := true endif endif .KATI_READONLY := BUILDING_INIT_BOOT_IMAGE # Are we building a recovery image BUILDING_RECOVERY_IMAGE := ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) BUILDING_RECOVERY_IMAGE := true else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) # Set to true to build recovery resources for vendor_boot BUILDING_RECOVERY_IMAGE := true else ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY))) BUILDING_RECOVERY_IMAGE := true endif endif else ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),true) BUILDING_RECOVERY_IMAGE := true endif .KATI_READONLY := BUILDING_RECOVERY_IMAGE # Are we building a vendor boot image BUILDING_VENDOR_BOOT_IMAGE := ifdef BOARD_BOOT_HEADER_VERSION ifneq ($(call math_gt_or_eq,$(BOARD_BOOT_HEADER_VERSION),3),) ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),) BUILDING_VENDOR_BOOT_IMAGE := true else ifeq ($(PRODUCT_BUILD_VENDOR_BOOT_IMAGE),true) BUILDING_VENDOR_BOOT_IMAGE := true endif endif endif .KATI_READONLY := BUILDING_VENDOR_BOOT_IMAGE # Are we building a vendor kernel boot image BUILDING_VENDOR_KERNEL_BOOT_IMAGE := ifeq ($(PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE),true) ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true) $(error BUILDING_VENDOR_BOOT_IMAGE is required, but BUILDING_VENDOR_BOOT_IMAGE is not true) endif ifndef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE $(error BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE is required when PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE is true) endif BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true else ifeq ($(PRODUCT_BUILD_VENDOR_KERNEL),) ifdef BOARD_VENDOR_KERNEL_BOOTIMAGE_PARTITION_SIZE ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true) BUILDING_VENDOR_KERNEL_BOOT_IMAGE := true endif endif endif # end of PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE .KATI_READONLY := BUILDING_VENDOR_KERNEL_BOOT_IMAGE # Are we building a ramdisk image BUILDING_RAMDISK_IMAGE := true ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),) # TODO: Be smarter about this. This probably only needs to happen when one of the follow is true: # BUILDING_BOOT_IMAGE # BUILDING_RECOVERY_IMAGE else ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),false) BUILDING_RAMDISK_IMAGE := endif .KATI_READONLY := BUILDING_RAMDISK_IMAGE # Are we building a debug vendor_boot image BUILDING_DEBUG_VENDOR_BOOT_IMAGE := # Can't build vendor_boot-debug.img if we're not building a ramdisk. ifndef BUILDING_RAMDISK_IMAGE ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a ramdisk image. \ Skip building the debug vendor_boot image.) endif # Can't build vendor_boot-debug.img if we're not building a vendor_boot.img. else ifndef BUILDING_VENDOR_BOOT_IMAGE ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a vendor_boot image. \ Skip building the debug vendor_boot image.) endif else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),) BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true) BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true endif endif .KATI_READONLY := BUILDING_DEBUG_VENDOR_BOOT_IMAGE _has_boot_img_artifact := ifneq ($(strip $(TARGET_NO_KERNEL)),true) ifdef BUILDING_BOOT_IMAGE _has_boot_img_artifact := true endif # BUILDING_RECOVERY_IMAGE && BOARD_USES_RECOVERY_AS_BOOT implies that # recovery is being built with the file name *boot.img*, which still counts # as "building boot.img". ifdef BUILDING_RECOVERY_IMAGE ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) _has_boot_img_artifact := true endif endif endif # Are we building a debug boot image BUILDING_DEBUG_BOOT_IMAGE := # Can't build boot-debug.img if we're not building a ramdisk. ifndef BUILDING_RAMDISK_IMAGE ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a ramdisk image. \ Skip building the debug boot image.) endif # Can't build boot-debug.img if we're not building a boot.img. else ifndef _has_boot_img_artifact ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \ Skip building the debug boot image.) endif else ifdef BUILDING_INIT_BOOT_IMAGE ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we don't have a ramdisk in the boot image. \ Skip building the debug boot image.) endif else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),) BUILDING_DEBUG_BOOT_IMAGE := true # Don't build boot-debug.img if we're already building vendor_boot-debug.img. ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE BUILDING_DEBUG_BOOT_IMAGE := endif else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true) BUILDING_DEBUG_BOOT_IMAGE := true endif endif .KATI_READONLY := BUILDING_DEBUG_BOOT_IMAGE _has_boot_img_artifact := # Are we building a userdata image BUILDING_USERDATA_IMAGE := ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),) ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE BUILDING_USERDATA_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),true) BUILDING_USERDATA_IMAGE := true endif .KATI_READONLY := BUILDING_USERDATA_IMAGE # Are we building a vbmeta image BUILDING_VBMETA_IMAGE := true ifeq ($(PRODUCT_BUILD_VBMETA_IMAGE),false) BUILDING_VBMETA_IMAGE := endif .KATI_READONLY := BUILDING_VBMETA_IMAGE # Are we building a super_empty image BUILDING_SUPER_EMPTY_IMAGE := ifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),) ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) ifneq ($(BOARD_SUPER_PARTITION_SIZE),) BUILDING_SUPER_EMPTY_IMAGE := true endif endif else ifeq ($(PRODUCT_BUILD_SUPER_EMPTY_IMAGE),true) ifneq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)) $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but PRODUCT_USE_DYNAMIC_PARTITIONS is not true) endif ifeq ($(BOARD_SUPER_PARTITION_SIZE),) $(error PRODUCT_BUILD_SUPER_EMPTY_IMAGE set to true, but BOARD_SUPER_PARTITION_SIZE is not defined) endif BUILDING_SUPER_EMPTY_IMAGE := true endif .KATI_READONLY := BUILDING_SUPER_EMPTY_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR ifeq ($(TARGET_COPY_OUT_VENDOR),$(_vendor_path_placeholder)) TARGET_COPY_OUT_VENDOR := system/vendor else ifeq ($(filter vendor system/vendor,$(TARGET_COPY_OUT_VENDOR)),) $(error TARGET_COPY_OUT_VENDOR must be either 'vendor' or 'system/vendor', seeing '$(TARGET_COPY_OUT_VENDOR)'.) endif PRODUCT_COPY_FILES := $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),$(PRODUCT_COPY_FILES)) BOARD_USES_VENDORIMAGE := ifdef BOARD_PREBUILT_VENDORIMAGE BOARD_USES_VENDORIMAGE := true endif ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE BOARD_USES_VENDORIMAGE := true endif # TODO(b/137169253): For now, some AOSP targets build with prebuilt vendor image. # But target's BOARD_PREBUILT_VENDORIMAGE is not filled. ifeq ($(TARGET_COPY_OUT_VENDOR),vendor) BOARD_USES_VENDORIMAGE := true else ifdef BOARD_USES_VENDORIMAGE $(error TARGET_COPY_OUT_VENDOR must be set to 'vendor' to use a vendor image) endif .KATI_READONLY := BOARD_USES_VENDORIMAGE BUILDING_VENDOR_IMAGE := ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),) ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE BUILDING_VENDOR_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),true) BUILDING_VENDOR_IMAGE := true ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_VENDOR_IMAGE set to true, but BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_VENDORIMAGE BUILDING_VENDOR_IMAGE := endif .KATI_READONLY := BUILDING_VENDOR_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_PRODUCT ifeq ($(TARGET_COPY_OUT_PRODUCT),$(_product_path_placeholder)) TARGET_COPY_OUT_PRODUCT := system/product else ifeq ($(filter product system/product,$(TARGET_COPY_OUT_PRODUCT)),) $(error TARGET_COPY_OUT_PRODUCT must be either 'product' or 'system/product', seeing '$(TARGET_COPY_OUT_PRODUCT)'.) endif PRODUCT_COPY_FILES := $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),$(PRODUCT_COPY_FILES)) BOARD_USES_PRODUCTIMAGE := ifdef BOARD_PREBUILT_PRODUCTIMAGE BOARD_USES_PRODUCTIMAGE := true endif ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE BOARD_USES_PRODUCTIMAGE := true endif $(call check_image_config,product) .KATI_READONLY := BOARD_USES_PRODUCTIMAGE BUILDING_PRODUCT_IMAGE := ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),) ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE BUILDING_PRODUCT_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),true) BUILDING_PRODUCT_IMAGE := true ifndef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_PRODUCT_IMAGE set to true, but BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_PRODUCTIMAGE BUILDING_PRODUCT_IMAGE := endif .KATI_READONLY := BUILDING_PRODUCT_IMAGE ########################################### # TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will be set to # TARGET_COPY_OUT_PRODUCT as a workaround. TARGET_COPY_OUT_PRODUCT_SERVICES := $(TARGET_COPY_OUT_PRODUCT) ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_EXT ifeq ($(TARGET_COPY_OUT_SYSTEM_EXT),$(_system_ext_path_placeholder)) TARGET_COPY_OUT_SYSTEM_EXT := system/system_ext else ifeq ($(filter system_ext system/system_ext,$(TARGET_COPY_OUT_SYSTEM_EXT)),) $(error TARGET_COPY_OUT_SYSTEM_EXT must be either 'system_ext' or 'system/system_ext', seeing '$(TARGET_COPY_OUT_SYSTEM_EXT)'.) endif PRODUCT_COPY_FILES := $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),$(PRODUCT_COPY_FILES)) BOARD_USES_SYSTEM_EXTIMAGE := ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE BOARD_USES_SYSTEM_EXTIMAGE := true endif ifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE BOARD_USES_SYSTEM_EXTIMAGE := true endif $(call check_image_config,system_ext) .KATI_READONLY := BOARD_USES_SYSTEM_EXTIMAGE BUILDING_SYSTEM_EXT_IMAGE := ifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),) ifdef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE BUILDING_SYSTEM_EXT_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_SYSTEM_EXT_IMAGE),true) BUILDING_SYSTEM_EXT_IMAGE := true ifndef BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_SYSTEM_EXT_IMAGE set to true, but BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE BUILDING_SYSTEM_EXT_IMAGE := endif .KATI_READONLY := BUILDING_SYSTEM_EXT_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR_DLKM ifeq ($(TARGET_COPY_OUT_VENDOR_DLKM),$(_vendor_dlkm_path_placeholder)) TARGET_COPY_OUT_VENDOR_DLKM := $(TARGET_COPY_OUT_VENDOR)/vendor_dlkm else ifeq ($(filter vendor_dlkm system/vendor/vendor_dlkm vendor/vendor_dlkm,$(TARGET_COPY_OUT_VENDOR_DLKM)),) $(error TARGET_COPY_OUT_VENDOR_DLKM must be either 'vendor_dlkm', 'system/vendor/vendor_dlkm' or 'vendor/vendor_dlkm', seeing '$(TARGET_COPY_OUT_VENDOR_DLKM)'.) endif PRODUCT_COPY_FILES := $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),$(PRODUCT_COPY_FILES)) BOARD_USES_VENDOR_DLKMIMAGE := ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE BOARD_USES_VENDOR_DLKMIMAGE := true endif ifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE BOARD_USES_VENDOR_DLKMIMAGE := true endif $(call check_image_config,vendor_dlkm) BUILDING_VENDOR_DLKM_IMAGE := ifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),) ifdef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE BUILDING_VENDOR_DLKM_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_VENDOR_DLKM_IMAGE),true) BUILDING_VENDOR_DLKM_IMAGE := true ifndef BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_VENDOR_DLKM_IMAGE set to true, but BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE BUILDING_VENDOR_DLKM_IMAGE := endif .KATI_READONLY := BUILDING_VENDOR_DLKM_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_ODM ifeq ($(TARGET_COPY_OUT_ODM),$(_odm_path_placeholder)) TARGET_COPY_OUT_ODM := $(TARGET_COPY_OUT_VENDOR)/odm else ifeq ($(filter odm system/vendor/odm vendor/odm,$(TARGET_COPY_OUT_ODM)),) $(error TARGET_COPY_OUT_ODM must be either 'odm', 'system/vendor/odm' or 'vendor/odm', seeing '$(TARGET_COPY_OUT_ODM)'.) endif PRODUCT_COPY_FILES := $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),$(PRODUCT_COPY_FILES)) BOARD_USES_ODMIMAGE := ifdef BOARD_PREBUILT_ODMIMAGE BOARD_USES_ODMIMAGE := true endif ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE BOARD_USES_ODMIMAGE := true endif $(call check_image_config,odm) BUILDING_ODM_IMAGE := ifeq ($(PRODUCT_BUILD_ODM_IMAGE),) ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE BUILDING_ODM_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_ODM_IMAGE),true) BUILDING_ODM_IMAGE := true ifndef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_ODM_IMAGE set to true, but BOARD_ODMIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_ODMIMAGE BUILDING_ODM_IMAGE := endif .KATI_READONLY := BUILDING_ODM_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_ODM_DLKM ifeq ($(TARGET_COPY_OUT_ODM_DLKM),$(_odm_dlkm_path_placeholder)) TARGET_COPY_OUT_ODM_DLKM := $(TARGET_COPY_OUT_VENDOR)/odm_dlkm else ifeq ($(filter odm_dlkm system/vendor/odm_dlkm vendor/odm_dlkm,$(TARGET_COPY_OUT_ODM_DLKM)),) $(error TARGET_COPY_OUT_ODM_DLKM must be either 'odm_dlkm', 'system/vendor/odm_dlkm' or 'vendor/odm_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.) endif PRODUCT_COPY_FILES := $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),$(PRODUCT_COPY_FILES)) BOARD_USES_ODM_DLKMIMAGE := ifdef BOARD_PREBUILT_ODM_DLKMIMAGE BOARD_USES_ODM_DLKMIMAGE := true endif ifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE BOARD_USES_ODM_DLKMIMAGE := true endif $(call check_image_config,odm_dlkm) BUILDING_ODM_DLKM_IMAGE := ifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),) ifdef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE BUILDING_ODM_DLKM_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_ODM_DLKM_IMAGE),true) BUILDING_ODM_DLKM_IMAGE := true ifndef BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_ODM_DLKM_IMAGE set to true, but BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_ODM_DLKMIMAGE BUILDING_ODM_DLKM_IMAGE := endif .KATI_READONLY := BUILDING_ODM_DLKM_IMAGE ########################################### # Now we can substitute with the real value of TARGET_COPY_OUT_SYSTEM_DLKM ifeq ($(TARGET_COPY_OUT_SYSTEM_DLKM),$(_system_dlkm_path_placeholder)) TARGET_COPY_OUT_SYSTEM_DLKM := $(TARGET_COPY_OUT_SYSTEM)/system_dlkm else ifeq ($(filter system_dlkm system/system_dlkm,$(TARGET_COPY_OUT_SYSTEM_DLKM)),) $(error TARGET_COPY_OUT_SYSTEM_DLKM must be either 'system_dlkm' or 'system/system_dlkm', seeing '$(TARGET_COPY_OUT_ODM_DLKM)'.) endif PRODUCT_COPY_FILES := $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),$(PRODUCT_COPY_FILES)) BOARD_USES_SYSTEM_DLKMIMAGE := ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE BOARD_USES_SYSTEM_DLKMIMAGE := true endif ifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE BOARD_USES_SYSTEM_DLKMIMAGE := true endif $(call check_image_config,system_dlkm) BUILDING_SYSTEM_DLKM_IMAGE := ifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),) ifdef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE BUILDING_SYSTEM_DLKM_IMAGE := true endif else ifeq ($(PRODUCT_BUILD_SYSTEM_DLKM_IMAGE),true) BUILDING_SYSTEM_DLKM_IMAGE := true ifndef BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE $(error PRODUCT_BUILD_SYSTEM_DLKM_IMAGE set to true, but BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE not defined) endif endif ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE BUILDING_SYSTEM_DLKM_IMAGE := endif .KATI_READONLY := BUILDING_SYSTEM_DLKM_IMAGE BOARD_USES_PVMFWIMAGE := ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true) BOARD_USES_PVMFWIMAGE := true endif .KATI_READONLY := BOARD_USES_PVMFWIMAGE BOARD_USES_DESKTOP_RECOVERY_IMAGE := ifeq ($(PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE),true) BOARD_USES_DESKTOP_RECOVERY_IMAGE := true endif .KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_IMAGE BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL := ifeq ($(PRODUCT_USES_DESKTOP_RECOVERY_SWAP_KERNEL),true) BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL := true endif .KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_SWAP_KERNEL BOARD_USES_DESKTOP_UPDATE_IMAGE := ifeq ($(PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE),true) BOARD_USES_DESKTOP_UPDATE_IMAGE := true endif .KATI_READONLY := BOARD_USES_DESKTOP_UPDATE_IMAGE ########################################### # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE. TARGET_RECOVERY_UPDATER_LIBS ?= ifeq ($(AB_OTA_UPDATER),) AB_OTA_UPDATER := true endif .KATI_READONLY := TARGET_RECOVERY_UPDATER_LIBS AB_OTA_UPDATER # Ensure that if PRODUCT_OTA_FORCE_NON_AB_PACKAGE == true, then AB_OTA_UPDATER must be true ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) ifneq ($(AB_OTA_UPDATER),true) $(error AB_OTA_UPDATER must be set to true when PRODUCT_OTA_FORCE_NON_AB_PACKAGE is true) endif endif # In some configurations, A/B and non-A/B may coexist. Check TARGET_OTA_ALLOW_NON_AB # to see if non-A/B is supported. TARGET_OTA_ALLOW_NON_AB := false ifneq ($(AB_OTA_UPDATER),true) TARGET_OTA_ALLOW_NON_AB := true else ifeq ($(PRODUCT_OTA_FORCE_NON_AB_PACKAGE),true) TARGET_OTA_ALLOW_NON_AB := true endif .KATI_READONLY := TARGET_OTA_ALLOW_NON_AB ifneq ($(TARGET_OTA_ALLOW_NON_AB),true) ifneq ($(strip $(TARGET_RECOVERY_UPDATER_LIBS)),) $(error Do not use TARGET_RECOVERY_UPDATER_LIBS when using TARGET_OTA_ALLOW_NON_AB) endif endif # For Non A/B full OTA, disable brotli compression. ifeq ($(TARGET_OTA_ALLOW_NON_AB),true) BOARD_NON_AB_OTA_DISABLE_COMPRESSION := true endif # Quick check for building generic OTA packages. Currently it only supports A/B OTAs. ifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true) ifneq ($(AB_OTA_UPDATER),true) $(error PRODUCT_BUILD_GENERIC_OTA_PACKAGE with 'AB_OTA_UPDATER != true' is not supported) endif endif ifdef BOARD_PREBUILT_DTBIMAGE_DIR ifneq ($(BOARD_INCLUDE_DTB_IN_BOOTIMG),true) $(error BOARD_PREBUILT_DTBIMAGE_DIR with 'BOARD_INCLUDE_DTB_IN_BOOTIMG != true' is not supported) endif endif # Check BOARD_VNDK_VERSION define check_vndk_version $(eval vndk_path := prebuilts/vndk/v$(1)) \ $(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found)) endef TARGET_VENDOR_TEST_SUFFIX := /vendor ifeq (,$(TARGET_BUILD_UNBUNDLED)) ifdef PRODUCT_EXTRA_VNDK_VERSIONS $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v))) endif endif # Ensure that BOARD_SYSTEMSDK_VERSIONS are all within PLATFORM_SYSTEMSDK_VERSIONS _unsupported_systemsdk_versions := $(filter-out $(PLATFORM_SYSTEMSDK_VERSIONS),$(BOARD_SYSTEMSDK_VERSIONS)) ifneq (,$(_unsupported_systemsdk_versions)) $(error System SDK versions '$(_unsupported_systemsdk_versions)' in BOARD_SYSTEMSDK_VERSIONS are not supported.\ Supported versions are $(PLATFORM_SYSTEMSDK_VERSIONS)) endif ########################################### # BOARD_API_LEVEL for vendor API surface ifdef RELEASE_BOARD_API_LEVEL ifdef BOARD_API_LEVEL $(error BOARD_API_LEVEL must not be set manually. The build system automatically sets this value.) endif BOARD_API_LEVEL := $(RELEASE_BOARD_API_LEVEL) .KATI_READONLY := BOARD_API_LEVEL endif ########################################### # Handle BUILD_BROKEN_USES_BUILD_* $(foreach m,$(DEFAULT_WARNING_BUILD_MODULE_TYPES),\ $(if $(filter false,$(BUILD_BROKEN_USES_$(m))),\ $(KATI_obsolete_var $(m),Please convert to Soong),\ $(KATI_deprecated_var $(m),Please convert to Soong))) $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers),\ $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers)) $(foreach m,$(filter-out BUILD_COPY_HEADERS,$(DEFAULT_ERROR_BUILD_MODULE_TYPES)),\ $(if $(filter true,$(BUILD_BROKEN_USES_$(m))),\ $(KATI_deprecated_var $(m),Please convert to Soong),\ $(KATI_obsolete_var $(m),Please convert to Soong))) ifndef BUILDING_RECOVERY_IMAGE ifeq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)) $(error Should not set BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE if not building recovery image) endif endif ifndef BUILDING_VENDOR_BOOT_IMAGE ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) $(error Should not set BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT if not building vendor_boot image) endif ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if not building vendor_boot image) endif else # BUILDING_VENDOR_BOOT_IMAGE ifneq (,$(call math_lt,$(BOARD_BOOT_HEADER_VERSION),4)) ifdef BOARD_VENDOR_RAMDISK_FRAGMENTS $(error Should not set BOARD_VENDOR_RAMDISK_FRAGMENTS if \ BOARD_BOOT_HEADER_VERSION is less than 4) endif ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \ BOARD_BOOT_HEADER_VERSION is less than 4) endif endif endif # BUILDING_VENDOR_BOOT_IMAGE ifneq ($(words $(BOARD_VENDOR_RAMDISK_FRAGMENTS)),$(words $(sort $(BOARD_VENDOR_RAMDISK_FRAGMENTS)))) $(error BOARD_VENDOR_RAMDISK_FRAGMENTS has duplicate entries: $(BOARD_VENDOR_RAMDISK_FRAGMENTS)) endif ifeq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT)) ifneq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) $(error Should not set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT if \ BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is not set) endif endif # If BOARD_USES_GENERIC_KERNEL_IMAGE is set, BOARD_USES_RECOVERY_AS_BOOT must not be set. # Devices without a dedicated recovery partition uses BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT to # build recovery into vendor_boot. ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE)) ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) $(error BOARD_USES_RECOVERY_AS_BOOT cannot be true if BOARD_USES_GENERIC_KERNEL_IMAGE is true. \ Use BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT instead) endif endif ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT)) ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) $(error BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT and BOARD_USES_RECOVERY_AS_BOOT cannot be \ both true. Recovery resources should be installed to either boot or vendor_boot, but not both) endif endif ================================================ FILE: core/board_config_wifi.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ############################################################### # This file adds WIFI variables into soong config namespace (`wifi`) # ############################################################### ifdef BOARD_WLAN_DEVICE $(call soong_config_set,wifi,board_wlan_device,$(BOARD_WLAN_DEVICE)) endif ifdef WIFI_DRIVER_MODULE_PATH $(call soong_config_set,wifi,driver_module_path,$(WIFI_DRIVER_MODULE_PATH)) endif ifdef WIFI_DRIVER_MODULE_ARG $(call soong_config_set,wifi,driver_module_arg,$(WIFI_DRIVER_MODULE_ARG)) endif ifdef WIFI_DRIVER_MODULE_NAME $(call soong_config_set,wifi,driver_module_name,$(WIFI_DRIVER_MODULE_NAME)) endif ifdef WIFI_DRIVER_FW_PATH_STA $(call soong_config_set,wifi,driver_fw_path_sta,$(WIFI_DRIVER_FW_PATH_STA)) endif ifdef WIFI_DRIVER_FW_PATH_AP $(call soong_config_set,wifi,driver_fw_path_ap,$(WIFI_DRIVER_FW_PATH_AP)) endif ifdef WIFI_DRIVER_FW_PATH_P2P $(call soong_config_set,wifi,driver_fw_path_p2p,$(WIFI_DRIVER_FW_PATH_P2P)) endif ifdef WIFI_DRIVER_FW_PATH_PARAM $(call soong_config_set,wifi,driver_fw_path_param,$(WIFI_DRIVER_FW_PATH_PARAM)) endif ifdef WIFI_DRIVER_STATE_CTRL_PARAM $(call soong_config_set,wifi,driver_state_ctrl_param,$(WIFI_DRIVER_STATE_CTRL_PARAM)) endif ifdef WIFI_DRIVER_STATE_ON $(call soong_config_set,wifi,driver_state_on,$(WIFI_DRIVER_STATE_ON)) endif ifdef WIFI_DRIVER_STATE_OFF $(call soong_config_set,wifi,driver_state_off,$(WIFI_DRIVER_STATE_OFF)) endif ifdef WIFI_MULTIPLE_VENDOR_HALS $(call soong_config_set,wifi,multiple_vendor_hals,$(WIFI_MULTIPLE_VENDOR_HALS)) endif ifneq ($(wildcard vendor/google/libraries/GoogleWifiConfigLib),) $(call soong_config_set,wifi,google_wifi_config_lib,true) endif ifdef WIFI_HAL_INTERFACE_COMBINATIONS $(call soong_config_set,wifi,hal_interface_combinations,$(WIFI_HAL_INTERFACE_COMBINATIONS)) endif ifdef WIFI_HIDL_FEATURE_AWARE $(call soong_config_set,wifi,hidl_feature_aware,true) endif ifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE $(call soong_config_set,wifi,hidl_feature_dual_interface,true) endif ifdef WIFI_HIDL_FEATURE_DISABLE_AP $(call soong_config_set,wifi,hidl_feature_disable_ap,true) endif ifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION $(call soong_config_set,wifi,hidl_feature_disable_ap_mac_randomization,true) endif ifdef WIFI_AVOID_IFACE_RESET_MAC_CHANGE $(call soong_config_set,wifi,avoid_iface_reset_mac_change,true) endif ifdef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN $(call soong_config_set,wifi,wifi_skip_state_toggle_off_on_for_nan,true) endif ifeq ($(strip $(TARGET_USES_AOSP_FOR_WLAN)),true) $(call soong_config_set,wifi,target_uses_aosp_for_wlan,true) endif ================================================ FILE: core/board_config_wpa_supplicant.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ############################################################### # This file adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`) # ############################################################### ifdef BOARD_HOSTAPD_DRIVER $(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true) ifneq ($(BOARD_HOSTAPD_DRIVER),NL80211) $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!) endif endif ifdef BOARD_WPA_SUPPLICANT_DRIVER ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211) $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!) endif endif # This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA # And it is only used for a cflags setting in driver. $(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE)) # Belong to CONFIG_IEEE80211AX definition ifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true) $(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true) endif # Belong to CONFIG_IEEE80211BE definition ifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true) $(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true) endif # PLATFORM_VERSION $(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION)) # BOARD_HOSTAPD_PRIVATE_LIB ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),) $(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true) else $(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB)) endif ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true) $(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true) endif ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),) $(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true) endif # BOARD_WPA_SUPPLICANT_PRIVATE_LIB ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) $(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true) else $(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)) endif ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),) $(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true) endif ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled) $(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true) endif ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true) $(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true) endif # New added in internal main ifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled) $(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true) endif ================================================ FILE: core/build-system.html ================================================ Android Build System

Android Build System

Status: Draft   (as of May 18, 2006)

Contents

Objective

The primary goals of reworking the build system are (1) to make dependencies work more reliably, so that when files need to rebuilt, they are, and (2) to improve performance of the build system so that unnecessary modules are not rebuilt, and so doing a top-level build when little or nothing needs to be done for a build takes as little time as possible.

Principles and Use Cases and Policy

Given the above objective, these are the overall principles and use cases that we will support. This is not an exhaustive list.

Multiple Targets

It needs to be possible to build the Android platform for multiple targets. This means:

  • The build system will support building tools for the host platform, both ones that are used in the build process itself, and developer tools like the simulator.
  • The build system will need to be able to build tools on Linux (definitely Goobuntu and maybe Grhat), MacOS, and to some degree on Windows.
  • The build system will need to be able to build the OS on Linux, and in the short-term, MacOS. Note that this is a conscious decision to stop building the OS on Windows. We are going to rely on the emulator there and not attempt to use the simulator. This is a requirement change now that the emulator story is looking brighter.

Non-Recursive Make

To achieve the objectives, the build system will be rewritten to use make non-recursively. For more background on this, read Recursive Make Considered Harmful. For those that don't want PDF, here is the Google translated version.

Rapid Compile-Test Cycles

When developing a component, for example a C++ shared library, it must be possible to easily rebuild just that component, and not have to wait more than a couple seconds for dependency checks, and not have to wait for unneeded components to be built.

Both Environment and Config File Based Settings

To set the target, and other options, some people on the team like to have a configuration file in a directory so they do not have an environment setup script to run, and others want an environment setup script to run so they can run builds in different terminals on the same tree, or switch back and forth in one terminal. We will support both.

Object File Directory / make clean

Object files and other intermediate files will be generated into a directory that is separate from the source tree. The goal is to have make clean be "rm -rf " in the tree root directory. The primary goals of this are to simplify searching the source tree, and to make "make clean" more reliable.

SDK

The SDK will be a tarball that will allow non-OS-developers to write apps. The apps will actually be built by first building the SDK, and then building the apps against that SDK. This will hopefully (1) make writing apps easier for us, because we won't have to rebuild the OS as much, and we can use the standard java-app development tools, and (2) allow us to dog-food the SDK, to help ensure its quality. Cedric has suggested (and I agree) that apps built from the SDK should be built with ant. Stay tuned for more details as we figure out exactly how this will work.

Dependecies

Dependencies should all be automatic. Unless there is a custom tool involved (e.g. the webkit has several), the dependencies for shared and static libraries, .c, .cpp, .h, .java, java libraries, etc., should all work without intervention in the Android.mk file.

Wildcard source files

Wildcarding source file will be discouraged. It may be useful in some scenarios. The default $(wildcard *) will not work due to the current directory being set to the root of the build tree.

Multiple targets in one directory

It will be possible to generate more than one target from a given subdirectory. For example, libutils generates a shared library for the target and a static library for the host.

Makefile fragments for modules

Android.mk is the standard name for the makefile fragments that control the building of a given module. Only the top directory should have a file named "Makefile".

Use shared libraries

Currently, the simulator is not built to use shared libraries. This should be fixed, and now is a good time to do it. This implies getting shared libraries to work on Mac OS.

Nice to Have

These things would be nice to have, and this is a good place to record them, however these are not promises.

Simultaneous Builds

The hope is to be able to do two builds for different combos in the same tree at the same time, but this is a stretch goal, not a requirement. Doing two builds in the same tree, not at the same time must work. (update: it's looking like we'll get the two builds at the same time working)

Deleting headers (or other dependecies)

Problems can arise if you delete a header file that is referenced in ".d" files. The easy way to deal with this is "make clean". There should be a better way to handle it. (from fadden)

One way of solving this is introducing a dependency on the directory. The problem is that this can create extra dependecies and slow down the build. It's a tradeoff.

Multiple builds

General way to perform builds across the set of known platforms. This would make it easy to perform multiple platform builds when testing a change, and allow a wide-scale "make clean". Right now the buildspec.mk or environment variables need to be updated before each build. (from fadden)

Aftermarket Locales and Carrier

We will eventually need to add support for creating locales and carrier customizations to the SDK, but that will not be addressed right now.

Usage

You've read (or scrolled past) all of the motivations for this build system, and you want to know how to use it. This is the place.

Your first build

The Building document describes how do do builds.

build/envsetup.sh functions

If you source the file build/envsetup.sh into your bash environment, . build/envsetup.shyou'll get a few helpful shell functions:
  • printconfig - Prints the current configuration as set by the lunch and choosecombo commands.
  • m - Runs make from the top of the tree. This is useful because you can run make from within subdirectories. If you have the TOP environment variable set, it uses that. If you don't, it looks up the tree from the current directory, trying to find the top of the tree.
  • croot - cd to the top of the tree.
  • sgrep - grep for the regex you provide in all .c, .cpp, .h, .java, and .xml files below the current directory.

Build flavors/types

When building for a particular product, it's often useful to have minor variations on what is ultimately the final release build. These are the currently-defined "flavors" or "types" (we need to settle on a real name for these).

eng This is the default flavor. A plain "make" is the same as "make eng". droid is an alias for eng.
  • Installs modules tagged with: eng, debug, user, and/or development.
  • Installs non-APK modules that have no tags specified.
  • Installs APKs according to the product definition files, in addition to tagged APKs.
  • ro.secure=0
  • ro.debuggable=1
  • ro.kernel.android.checkjni=1
  • adb is enabled by default.
user "make user"

This is the flavor intended to be the final release bits.

  • Installs modules tagged with user.
  • Installs non-APK modules that have no tags specified.
  • Installs APKs according to the product definition files; tags are ignored for APK modules.
  • ro.adb.secure=1
  • ro.secure=1
  • ro.debuggable=0
  • adb is disabled by default.
userdebug "make userdebug"

The same as user, except:

  • Also installs modules tagged with debug.
  • ro.debuggable=1
  • adb is enabled by default.

If you build one flavor and then want to build another, you should run "make installclean" between the two makes to guarantee that you don't pick up files installed by the previous flavor. "make clean" will also suffice, but it takes a lot longer.

More pseudotargets

Sometimes you want to just build one thing. The following pseudotargets are there for your convenience:

  • droid - make droid is the normal build. This target is here because the default target has to have a name.
  • all - make all builds everything make droid does, plus everything whose LOCAL_MODULE_TAGS do not include the "droid" tag. The build server runs this to make sure that everything that is in the tree and has an Android.mk builds.
  • clean-$(LOCAL_MODULE) and clean-$(LOCAL_PACKAGE_NAME) - Let you selectively clean one target. For example, you can type make clean-libutils and it will delete libutils.so and all of the intermediate files, or you can type make clean-Home and it will clean just the Home app.
  • clean - make clean deletes all of the output and intermediate files for this configuration. This is the same as rm -rf out/<configuration>/
  • clobber - make clobber deletes all of the output and intermediate files for all configurations. This is the same as rm -rf out/.
  • dataclean - make dataclean deletes contents of the data directory inside the current combo directory. This is especially useful on the simulator and emulator, where the persistent data remains present between builds.
  • LOCAL_MODULE - Anything you specify as a LOCAL_MODULE in an Android.mk is made into a pseudotarget. For example, make runtime might be shorthand for make out/linux-x86-debug/system/bin/runtime (which would work), and make libkjs might be shorthand for make out/linux-x86-debug/system/lib/libkjs.so (which would also work).
  • targets - make targets will print a list of all of the LOCAL_MODULE names you can make.

How to add another component to the build - Android.mk templates

You have a new library, a new app, or a new executable. For each of the common types of modules, there is a corresponding file in the templates directory. It will usually be enough to copy one of these, and fill in your own values. Some of the more esoteric values are not included in the templates, but are instead just documented here, as is the documentation on using custom tools to generate files.

Mostly, you can just look for the TODO comments in the templates and do what it says. Please remember to delete the TODO comments when you're done to keep the files clean. The templates have minimal documentation in them, because they're going to be copied, and when that gets stale, the copies just won't get updated. So read on...

Apps

Use the templates/apps file.

This template is pretty self-explanitory. See the variables below for more details.

Java Libraries

Use the templates/java_library file.

The interesting thing here is the value of LOCAL_MODULE, which becomes the name of the jar file. (Actually right now, we're not making jar files yet, just directories of .class files, but the directory is named according to what you put in LOCAL_MODULE). This name will be what goes in the LOCAL_JAVA_LIBRARIES variable in modules that depend on your java library.

C/C++ Executables

Use the templates/executable file, or the templates/executable_host file.

This template has a couple extra options that you usually don't need. Please delete the ones you don't need, and remove the TODO comments. It makes the rest of them easier to read, and you can always refer back to the templates if you need them again later.

By default, on the target these are built into /system/bin, and on the host, they're built into /host/bin. These can be overridden by setting LOCAL_MODULE_PATH or LOCAL_MODULE_RELATIVE_PATH. See Putting targets elsewhere for more.

Shared Libraries

Use the templates/shared_library file, or the templates/shared_library_host file.

Remember that on the target, we use shared libraries, and on the host, we use static libraries, since executable size isn't as big an issue, and it simplifies distribution in the SDK.

Static Libraries

Use the templates/static_library file, or the templates/static_library_host file.

Remember that on the target, we use shared libraries, and on the host, we use static libraries, since executable size isn't as big an issue, and it simplifies distribution in the SDK.

Using Custom Tools

If you have a tool that generates source files for you, it's possible to have the build system get the dependencies correct for it. Here are a couple of examples. $@ is the make built-in variable for "the current target." The red parts are the parts you'll need to change.

You need to put this after you have declared LOCAL_PATH and LOCAL_MODULE, because the $(local-generated-sources-dir) and $(local-host-generated-sources-dir) macros use these variables to determine where to put the files.

Example 1

Here, there is one generated file, called chartables.c, which doesn't depend on anything. And is built by the tool built to $(HOST_OUT_EXECUTABLES)/dftables. Note on the second to last line that a dependency is created on the tool.

intermediates:= $(local-generated-sources-dir)
GEN := $(intermediates)/chartables.c
$(GEN): PRIVATE_CUSTOM_TOOL = $(HOST_OUT_EXECUTABLES)/dftables $@
$(GEN): $(HOST_OUT_EXECUTABLES)/dftables
	$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
Example 2

Here as a hypothetical example, we use use cat as if it were to transform a file. Pretend that it does something useful. Note how we use a target-specific variable called PRIVATE_INPUT_FILE to store the name of the input file.

intermediates:= $(local-generated-sources-dir)
GEN := $(intermediates)/file.c
$(GEN): PRIVATE_INPUT_FILE := $(LOCAL_PATH)/input.file
$(GEN): PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_INPUT_FILE) > $@
$(GEN): $(LOCAL_PATH)/input.file
	$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
Example 3

If you have several files that are all similar in name, and use the same tool, you can combine them. (here the *.lut.h files are the generated ones, and the *.cpp files are the input files)

intermediates:= $(local-generated-sources-dir)
GEN := $(addprefix $(intermediates)/kjs/, \
            array_object.lut.h \
            bool_object.lut.h \
        )
$(GEN): PRIVATE_CUSTOM_TOOL = perl libs/WebKitLib/WebKit/JavaScriptCore/kjs/create_hash_table $< -i > $@
$(GEN): $(intermediates)/%.lut.h : $(LOCAL_PATH)/%.cpp
	$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)

Unbundled build

Unbundled build has several meanings by the context. Let me explain the meaning by the flags related to "unbundled build"

TARGET_BUILD_UNBUNDLED

The source tree might not have the full platform sources. It turns on TARGET_BUILD_USE_PREBUILT_SDKS, unless UNBUNDLED_BUILD_SDKS_FROM_SOURCE is set. It is always set if TARGET_BUILD_APPS or TARGET_BUILD_UNBUNDLED_IMAGE is set.

TARGET_BUILD_USE_PREBUILT_SDKS

It is an internal flag. If it is set, prebuilt SDKs are used, even if a module's LOCAL_SDK_VERSION is current (including system_current, core_current, and so on). If it is unset, build current SDKs, and use them as usual.

DISABLE_PREOPT

It is an internal flag as well. If it is set, dexpreopt is disabled. It is always set if TARGET_BUILD_APPS or TARGET_BUILD_UNBUNDLED_IMAGE is set, because dexpreopt tightly depends on the platform.

TARGET_BUILD_APPS

Build the apps that can be distributed outside the platform, so it turns on TARGET_BUILD_UNBUNDLED and DISABLE_PREOPT. Also, it turns on TARGET_BUILD_USE_PREBUILT_SDKS, unless UNBUNDLED_BUILD_SDKS_FROM_SOURCE is set.

TARGET_BUILD_UNBUNDLED_IMAGE

It is similar to TARGET_BUILD_APPS, but its target is an unbundled partition (such as the vendor partition). Accordingly, it sets TARGET_BUILD_UNBUNDLED and DISABLE_PREOPT. We can call the partition unbundled, because the partition can be distributed outside the platform. And also, it turns on TARGET_BUILD_USE_PREBUILT_SDKS, unless UNBUNDLED_BUILD_SDKS_FROM_SOURCE is set.

Platform specific conditionals

Sometimes you need to set flags specifically for different platforms. Here is a list of which values the different build-system defined variables will be set to and some examples.

HOST_OS
linux
darwin
HOST_ARCH
x86
x86_64
HOST_BUILD_TYPE
release
debug
TARGET_ARCH
arm
arm64
x86
x86_64
TARGET_BUILD_TYPE
release
debug

There are also special variables to use instead of conditionals. Many of the normal variables (LOCAL_SRC_FILES, LOCAL_CFLAGS, etc) can be conditionally added to with _{arch} _{32|64}, and for the host, _{os}.

Some Examples

ifeq ($(TARGET_BUILD_TYPE),release)
LOCAL_CFLAGS += -DNDEBUG=1
endif

LOCAL_CFLAGS_arm += -DTARGET_IS_ARM

LOCAL_CFLAGS_64 += -DBIG_POINTER

# from libutils
# Use the futex based mutex and condition variable
# implementation from android-arm because it's shared mem safe
LOCAL_SRC_FILES_linux += futex_synchro.c
LOCAL_LDLIBS_linux += -lrt -ldl

Putting modules elsewhere

If you have modules that normally go somewhere, and you need to have them build somewhere else, read this.

If you have modules that need to go in a subdirectory of their normal location, for example HAL modules that need to go in /system/lib/hw or /vendor/lib/hw, set LOCAL_MODULE_RELATIVE_PATH in your Android.mk, for example:

LOCAL_MODULE_RELATIVE_PATH := hw

If you have modules that need to go in an entirely different location, for example the root filesystem instead of in /system, add these lines to your Android.mk:

LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)

For executables and libraries, you need to specify a LOCAL_UNSTRIPPED_PATH location if you specified a LOCAL_MODULE_PATH, because on target builds, we keep the unstripped executables so GDB can find the symbols. LOCAL_UNSTRIPPED_PATH is not necessary if you only specified LOCAL_MODULE_RELATIVE_PATH.

Look in core/envsetup.mk for all of the variables defining places to build things.

Android.mk variables

These are the variables that you'll commonly see in Android.mk files, listed alphabetically.

But first, a note on variable naming:

LOCAL_ANNOTATION_PROCESSORS

Set this to a list of modules built with BUILD_HOST_JAVA_LIBRARY to have their jars passed to javac with -processorpath for use as annotation processors.

LOCAL_ANNOTATION_PROCESSOR_CLASSES

Set this to a list of classes to be passed to javac as -processor arguments. This list is would be unnecessary, as javac will autodetect annotation processor classes, except that the Grok tool that is used on the Android source code does not autodetect them and requires listing them manually.

LOCAL_ASSET_FILES

In Android.mk files that include $(BUILD_PACKAGE) set this to the set of files you want built into your app. Usually:

LOCAL_ASSET_FILES += $(call find-subdir-assets)

This will probably change when we switch to ant for the apps' build system.

LOCAL_CC

If you want to use a different C compiler for this module, set LOCAL_CC to the path to the compiler. If LOCAL_CC is blank, the appropriate default compiler is used.

LOCAL_CXX

If you want to use a different C++ compiler for this module, set LOCAL_CXX to the path to the compiler. If LOCAL_CXX is blank, the appropriate default compiler is used.

LOCAL_CFLAGS

If you have additional flags to pass into the C or C++ compiler, add them here. For example:

LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1

LOCAL_CPPFLAGS

If you have additional flags to pass into only the C++ compiler, add them here. For example:

LOCAL_CPPFLAGS += -ffriend-injection

LOCAL_CPPFLAGS is guaranteed to be after LOCAL_CFLAGS on the compile line, so you can use it to override flags listed in LOCAL_CFLAGS.

LOCAL_CPP_EXTENSION

If your C++ files end in something other than ".cpp", you can specify the custom extension here. For example:

LOCAL_CPP_EXTENSION := .cc

Note that all C++ files for a given module must have the same extension; it is not currently possible to mix different extensions.

LOCAL_NO_DEFAULT_COMPILER_FLAGS

Normally, the compile line for C and C++ files includes global include paths and global cflags. If LOCAL_NO_DEFAULT_COMPILER_FLAGS is non-empty, none of the default includes or flags will be used when compiling C and C++ files in this module. LOCAL_C_INCLUDES, LOCAL_CFLAGS, and LOCAL_CPPFLAGS will still be used in this case, as will any DEBUG_CFLAGS that are defined for the module.

LOCAL_COPY_HEADERS

This will be going away.

The set of files to copy to the install include tree. You must also supply LOCAL_COPY_HEADERS_TO.

This is going away because copying headers messes up the error messages, and may lead to people editing those headers instead of the correct ones. It also makes it easier to do bad layering in the system, which we want to avoid. We also aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any headers.

LOCAL_COPY_HEADERS_TO

This will be going away.

The directory within "include" to copy the headers listed in LOCAL_COPY_HEADERS to.

This is going away because copying headers messes up the error messages, and may lead to people editing those headers instead of the correct ones. It also makes it easier to do bad layering in the system, which we want to avoid. We also aren't doing a C/C++ SDK, so there is no ultimate requirement to copy any headers.

LOCAL_C_INCLUDES

Additional directories to instruct the C/C++ compilers to look for header files in. These paths are rooted at the top of the tree. Use LOCAL_PATH if you have subdirectories of your own that you want in the include paths. For example:

LOCAL_C_INCLUDES += extlibs/zlib-1.2.3
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src

You should not add subdirectories of include to LOCAL_C_INCLUDES, instead you should reference those files in the #include statement with their subdirectories. For example:

#include <utils/KeyedVector.h>
not #include <KeyedVector.h>

There are some components that are doing this wrong, and should be cleaned up.

LOCAL_MODULE_TAGS

Set LOCAL_MODULE_TAGS to any number of whitespace-separated tags. If the tag list is empty or contains droid, the module will get installed as part of a make droid. Otherwise, it will only get installed by running make <your-module> or with the make all pseudotarget.

LOCAL_REQUIRED_MODULES

Set LOCAL_REQUIRED_MODULES to any number of whitespace-separated module names, like "libblah" or "Email". If this module is installed, all of the modules that it requires will be installed as well. This can be used to, e.g., ensure that necessary shared libraries or providers are installed when a given app is installed.

LOCAL_FORCE_STATIC_EXECUTABLE

If your executable should be linked statically, set LOCAL_FORCE_STATIC_EXECUTABLE:=true. There is a very short list of libraries that we have in static form (currently only libc).

LOCAL_GENERATED_SOURCES

Files that you add to LOCAL_GENERATED_SOURCES will be automatically generated and then linked in when your module is built. See the Custom Tools template makefile for an example.

LOCAL_JAVACFLAGS

If you have additional flags to pass into the javac compiler, add them here. For example:

LOCAL_JAVACFLAGS += -Xlint:deprecation

LOCAL_ERROR_PRONE_FLAGS

If you have additional flags to pass into the error prone compiler, add them here. For example:

LOCAL_ERROR_PRONE_FLAGS += -Xep:ClassCanBeStatic:ERROR

LOCAL_JAVA_LIBRARIES

When linking Java apps and libraries, LOCAL_JAVA_LIBRARIES specifies which sets of java classes to include. Currently there are two of these: core and framework. In most cases, it will look like this:

LOCAL_JAVA_LIBRARIES := core framework

Note that setting LOCAL_JAVA_LIBRARIES is not necessary (and is not allowed) when building an APK with "include $(BUILD_PACKAGE)". The appropriate libraries will be included automatically.

LOCAL_LDFLAGS

You can pass additional flags to the linker by setting LOCAL_LDFLAGS. Keep in mind that the order of parameters is very important to ld, so test whatever you do on all platforms.

LOCAL_LDLIBS

LOCAL_LDLIBS allows you to specify additional libraries that are not part of the build for your executable or library. Specify the libraries you want in -lxxx format; they're passed directly to the link line. However, keep in mind that there will be no dependency generated for these libraries. It's most useful in simulator builds where you want to use a library preinstalled on the host. The linker (ld) is a particularly fussy beast, so it's sometimes necessary to pass other flags here if you're doing something sneaky. Some examples:

LOCAL_LDLIBS += -lcurses -lpthread
LOCAL_LDLIBS += -Wl,-z,origin

LOCAL_NO_MANIFEST

If your package doesn't have a manifest (AndroidManifest.xml), then set LOCAL_NO_MANIFEST:=true. The common resources package does this.

LOCAL_PACKAGE_NAME

LOCAL_PACKAGE_NAME is the name of an app. For example, Dialer, Contacts, etc. This will probably change or go away when we switch to an ant-based build system for the apps.

LOCAL_PATCH_MODULE (experimental option)

As of January 2018, you almost certainly don't need this option, so please ask and only use it if you understand what you're doing. This feature is experimental and may go away in future.

When compiling language level 9+ .java code in packages that are part of a a system module, LOCAL_PATCH_MODULE names the module that your sources and dependencies should be patched into. The Android runtime currently (Jan 2018) doesn't implement the JEP 261 module system so this option is only supported at compile time. It should only be needed to compile tests in packages that exist in libcore and which are inconvenient to move elsewhere.

LOCAL_PATH

The directory your Android.mk file is in. You can set it by putting the following as the first line in your Android.mk:

LOCAL_PATH := $(my-dir)

The my-dir macro uses the MAKEFILE_LIST variable, so you must call it before you include any other makefiles. Also, consider that any subdirectories you inlcude might reset LOCAL_PATH, so do your own stuff before you include them. This also means that if you try to write several include lines that reference LOCAL_PATH, it won't work, because those included makefiles might reset LOCAL_PATH.

LOCAL_POST_PROCESS_COMMAND

For host executables, you can specify a command to run on the module after it's been linked. You might have to go through some contortions to get variables right because of early or late variable evaluation:

module := $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
LOCAL_POST_PROCESS_COMMAND := /Developer/Tools/Rez -d __DARWIN__ -t APPL\
       -d __WXMAC__ -o $(module) Carbon.r

LOCAL_PREBUILT_EXECUTABLES

When including $(BUILD_MULTI_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to executables that you want copied. They're located automatically into the right bin directory.

LOCAL_PREBUILT_LIBS

When including $(BUILD_MULTI_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to libraries that you want copied. They're located automatically into the right lib directory.

LOCAL_SHARED_LIBRARIES

These are the libraries you directly link against. You don't need to pass transitively included libraries. Specify the name without the suffix:

LOCAL_SHARED_LIBRARIES := \
    libutils \
    libui \
    libaudio \
    libexpat \
    libsgl

LOCAL_SRC_FILES

The build system looks at LOCAL_SRC_FILES to know what source files to compile -- .cpp .c .y .l .java. For lex and yacc files, it knows how to correctly do the intermediate .h and .c/.cpp files automatically. If the files are in a subdirectory of the one containing the Android.mk, prefix them with the directory name:

LOCAL_SRC_FILES := \
    file1.cpp \
    dir/file2.cpp

LOCAL_STATIC_LIBRARIES

These are the static libraries that you want to include in your module. Mostly, we use shared libraries, but there are a couple of places, like host executables where we use static libraries instead.

LOCAL_STATIC_LIBRARIES := \
    libutils \
    libtinyxml

LOCAL_MODULE

LOCAL_MODULE is the name of what's supposed to be generated from your Android.mk. For exmample, for libkjs, the LOCAL_MODULE is "libkjs" (the build system adds the appropriate suffix -- .so .dylib .dll). For app modules, use LOCAL_PACKAGE_NAME instead of LOCAL_MODULE. We're planning on switching to ant for the apps, so this might become moot.

LOCAL_MODULE_PATH

Instructs the build system to put the module somewhere other than what's normal for its type. If you override this, make sure you also set LOCAL_UNSTRIPPED_PATH if it's an executable or a shared library so the unstripped binary has somewhere to go. An error will occur if you forget to.

See Putting modules elsewhere for more.

LOCAL_MODULE_RELATIVE_PATH

Instructs the build system to put the module in a subdirectory under the directory that is normal for its type. If you set this you do not need to set LOCAL_UNSTRIPPED_PATH, the unstripped binaries will also use the relative path.

See Putting modules elsewhere for more.

LOCAL_MODULE_HOST_OS

This specifies which OSes are supported by this host module. It is not used for target builds. The accepted values here are combinations of linux, darwin, and windows. By default, linux and darwin(MacOS) are considered to be supported. If a module should build under windows, you must specify windows, and any others to be supported. Some examples:

LOCAL_MODULE_HOST_OS := linux
LOCAL_MODULE_HOST_OS := darwin linux windows

LOCAL_UNSTRIPPED_PATH

Instructs the build system to put the unstripped version of the module somewhere other than what's normal for its type. Usually, you override this because you overrode LOCAL_MODULE_PATH for an executable or a shared library. If you overrode LOCAL_MODULE_PATH, but not LOCAL_UNSTRIPPED_PATH, an error will occur.

See Putting modules elsewhere for more.

LOCAL_WHOLE_STATIC_LIBRARIES

These are the static libraries that you want to include in your module without allowing the linker to remove dead code from them. This is mostly useful if you want to add a static library to a shared library and have the static library's content exposed from the shared library.

LOCAL_WHOLE_STATIC_LIBRARIES := \
    libsqlite3_android

LOCAL_YACCFLAGS

Any flags to pass to invocations of yacc for your module. A known limitation here is that the flags will be the same for all invocations of YACC for your module. This can be fixed. If you ever need it to be, just ask.

LOCAL_YACCFLAGS := -p kjsyy

Implementation Details

You should never have to touch anything in the config directory unless you're adding a new platform, new tools, or adding new features to the build system. In general, please consult with the build system owner(s) (android-build-team) before you go mucking around in here. That said, here are some notes on what's going on under the hood.

Environment Setup / buildspec.mk Versioning

In order to make easier for people when the build system changes, when it is necessary to make changes to buildspec.mk or to rerun the environment setup scripts, they contain a version number in the variable BUILD_ENV_SEQUENCE_NUMBER. If this variable does not match what the build system expects, it fails printing an error message explaining what happened. If you make a change that requires an update, you need to update two places so this message will be printed.

  • In core/envsetup.mk, increment the CORRECT_BUILD_ENV_SEQUENCE_NUMBER definition.
  • In buildspec.mk.default, update the BUILD_ENV_SEQUENCE_DUMBER definition to match the one in core/envsetup.mk
The scripts automatically get the value from the build system, so they will trigger the warning as well.

Additional makefile variables

You probably shouldn't use these variables. Please consult android-build-team before using them. These are mostly there for workarounds for other issues, or things that aren't completely done right.

LOCAL_ADDITIONAL_DEPENDENCIES

If your module needs to depend on anything else that isn't actually built in to it, you can add those make targets to LOCAL_ADDITIONAL_DEPENDENCIES. Usually this is a workaround for some other dependency that isn't created automatically.

LOCAL_BUILT_MODULE

This should not be used, since multiple binaries are now created from a single module defintiion.

When a module is built, the module is created in an intermediate directory then copied to its final location. LOCAL_BUILT_MODULE is the full path to the intermediate file. See LOCAL_INSTALLED_MODULE for the path to the final installed location of the module.

LOCAL_IS_HOST_MODULE

Set by the host_xxx.mk includes to tell base_rules.mk and the other includes that we're building for the host.

LOCAL_INSTALLED_MODULE

This should not be used, since multiple binaries are now created from a single module defintiion.

The fully qualified path name of the final location of the module. See LOCAL_BUILT_MODULE for the location of the intermediate file that the make rules should actually be constructing.

LOCAL_MODULE_CLASS

Which kind of module this is. This variable is used to construct other variable names used to locate the modules. See base_rules.mk and envsetup.mk.

LOCAL_MODULE_SUFFIX

The suffix that will be appended to LOCAL_MODULE to form LOCAL_MODULE_NAME. For example, .so, .a, .dylib.

LOCAL_STRIP_MODULE

If set to true (the default), the binary will be stripped and a debug link will be set up so that GDB will still work. If set to no_debuglink, the binary will be stripped, but no debug link will be added. If set to keep_symbols, it will strip the debug information, but keep the symbol table. Any other value will prevent stripping.

LOCAL_SYSTEM_SHARED_LIBRARIES

Used while building the base libraries: libc, libm, libdl. Usually it should be set to "none," as it is in $(CLEAR_VARS). When building these libraries, it's set to the ones they link against. For example, libc, libstdc++ and libdl don't link against anything, and libm links against libc. Normally, when the value is none, these libraries are automatically linked in to executables and libraries, so you don't need to specify them manually.

================================================ FILE: core/build_id.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # BUILD_ID is usually used to specify the branch name # (like "MAIN") or a branch name and a release candidate # (like "CRB01"). It must be a single word, and is # capitalized by convention. BUILD_ID=MAIN ================================================ FILE: core/build_rro_package.mk ================================================ ############################################################################# ## Standard rules for installing runtime resouce overlay APKs. ## ## Set LOCAL_RRO_THEME to the theme name if the package should apply only to ## a particular theme as set by ro.boot.vendor.overlay.theme system property. ## ## If LOCAL_RRO_THEME is not set, the package will apply always, independent ## of themes. ## ############################################################################# LOCAL_IS_RUNTIME_RESOURCE_OVERLAY := true ifneq ($(LOCAL_SRC_FILES),) $(error runtime resource overlay package should not contain sources) endif partition := ifeq ($(strip $(LOCAL_ODM_MODULE)),true) partition := $(TARGET_OUT_ODM) else ifeq ($(strip $(LOCAL_VENDOR_MODULE)),true) partition := $(TARGET_OUT_VENDOR) else ifeq ($(strip $(LOCAL_SYSTEM_EXT_MODULE)),true) partition := $(TARGET_OUT_SYSTEM_EXT) else partition := $(TARGET_OUT_PRODUCT) endif ifeq ($(LOCAL_RRO_THEME),) LOCAL_MODULE_PATH := $(partition)/overlay else LOCAL_MODULE_PATH := $(partition)/overlay/$(LOCAL_RRO_THEME) endif # Do not remove resources without default values nor dedupe resource # configurations with the same value LOCAL_AAPT_FLAGS += \ --no-resource-deduping \ --no-resource-removal partition := include $(BUILD_SYSTEM)/package.mk ================================================ FILE: core/cc_prebuilt_internal.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################ # Internal build rules for native prebuilt modules ############################################################ prebuilt_module_classes := STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS ifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),) $(call pretty-error,cc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only) endif my_strip_module := $(firstword \ $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ $(LOCAL_STRIP_MODULE)) ifeq (SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)) ifeq ($(LOCAL_IS_HOST_MODULE)$(my_strip_module),) # Strip but not try to add debuglink my_strip_module := no_debuglink endif endif ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),) prebuilt_module_is_a_library := true else prebuilt_module_is_a_library := endif # Don't install static libraries by default. ifndef LOCAL_UNINSTALLABLE_MODULE ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)) LOCAL_UNINSTALLABLE_MODULE := true endif endif my_check_elf_file_shared_lib_files := ifneq ($(filter true keep_symbols no_debuglink mini-debug-info,$(my_strip_module)),) ifdef LOCAL_IS_HOST_MODULE $(call pretty-error,Cannot strip/pack host module) endif ifeq ($(filter SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) $(call pretty-error,Can strip/pack only shared libraries or executables) endif # Set the arch-specific variables to set up the strip rules LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) := $(my_strip_module) include $(BUILD_SYSTEM)/dynamic_binary.mk built_module := $(linked_module) ifneq ($(LOCAL_SDK_VERSION),) # binary.mk filters out NDK_KNOWN_LIBS from my_shared_libs, thus those NDK libs are not added # to DEPENDENCIES_ON_SHARED_LIBRARIES. Assign $(my_ndk_shared_libraries_fullpath) to # my_check_elf_file_shared_lib_files so that check_elf_file.py can see those NDK stub libs. my_check_elf_file_shared_lib_files := $(my_ndk_shared_libraries_fullpath) endif else # my_strip_module not true include $(BUILD_SYSTEM)/base_rules.mk built_module := $(LOCAL_BUILT_MODULE) ifdef prebuilt_module_is_a_library EXPORTS_LIST += $(intermediates) EXPORTS.$(intermediates).FLAGS := $(foreach d,$(LOCAL_EXPORT_C_INCLUDE_DIRS),-I $(d)) EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS) include $(BUILD_SYSTEM)/allowed_ndk_types.mk ifdef LOCAL_SDK_VERSION my_link_type := native:ndk:$(my_ndk_stl_family):$(my_ndk_stl_link_type) else ifeq ($(call module-in-vendor-or-product),true) _name := $(patsubst %.vendor,%,$(LOCAL_MODULE)) _name := $(patsubst %.product,%,$(LOCAL_MODULE)) ifneq ($(filter $(_name),$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) $(LLNDK_LIBRARIES)),) ifeq ($(filter $(_name),$(VNDK_PRIVATE_LIBRARIES)),) my_link_type := native:vndk else my_link_type := native:vndk_private endif else ifeq ($(LOCAL_IN_PRODUCT),true) my_link_type := native:product else my_link_type := native:vendor endif endif else ifneq ($(filter $(TARGET_RECOVERY_OUT)/%,$(LOCAL_MODULE_PATH)),) my_link_type := native:recovery else my_link_type := native:platform endif # TODO: check dependencies of prebuilt files my_link_deps := my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := include $(BUILD_SYSTEM)/link_type.mk endif # prebuilt_module_is_a_library # The real dependency will be added after all Android.mks are loaded and the install paths # of the shared libraries are determined. ifdef LOCAL_INSTALLED_MODULE ifdef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) my_system_shared_libraries := else my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) endif else ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none) my_system_shared_libraries := libc libm libdl else my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) my_system_shared_libraries := $(patsubst libc,libc libdl,$(my_system_shared_libraries)) endif endif my_shared_libraries := $(strip \ $(filter-out $(my_system_shared_libraries),$(LOCAL_SHARED_LIBRARIES)) \ $(my_system_shared_libraries)) # Extra shared libraries introduced by LOCAL_CXX_STL (may append some libraries to # my_shared_libraries). include $(BUILD_SYSTEM)/cxx_stl_setup.mk # When compiling against API imported module, use API import stub libraries. apiimport_postfix := .apiimport ifeq ($(call module-in-vendor-or-product),true) ifeq ($(LOCAL_IN_PRODUCT),true) apiimport_postfix := .apiimport.product else apiimport_postfix := .apiimport.vendor endif endif ifdef my_shared_libraries my_shared_libraries := $(foreach l,$(my_shared_libraries), \ $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) endif #my_shared_libraries ifdef my_system_shared_libraries my_system_shared_libraries := $(foreach l,$(my_system_shared_libraries), \ $(if $(filter $(l), $(API_IMPORTED_SHARED_LIBRARIES)), $(l)$(apiimport_postfix), $(l))) endif #my_system_shared_libraries ifdef my_shared_libraries ifeq ($(call module-in-vendor-or-product),true) ifeq ($(LOCAL_IN_PRODUCT),true) my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) else my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) endif endif $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries)) endif # my_shared_libraries endif # LOCAL_INSTALLED_MODULE # We need to enclose the above export_includes and my_built_shared_libraries in # "my_strip_module not true" because otherwise the rules are defined in dynamic_binary.mk. endif # my_strip_module not true # Check prebuilt ELF binaries. include $(BUILD_SYSTEM)/check_elf_file.mk ifeq ($(NATIVE_COVERAGE),true) ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE))) $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).gcnodir)) ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) ifdef LOCAL_IS_HOST_MODULE my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) else my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) endif my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).gcnodir $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path))) $(LOCAL_BUILT_MODULE): $(my_coverage_path) endif else # Coverage information is needed when static lib is a dependency of another # coverage-enabled module. ifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS)) GCNO_ARCHIVE := $(LOCAL_MODULE).gcnodir $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_PREFIX := $(my_prefix) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX) $(intermediates)/$(GCNO_ARCHIVE) : $(transform-o-to-static-lib) endif endif endif $(built_module) : $(my_prebuilt_src_file) $(transform-prebuilt-to-target) ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) $(hide) chmod +x $@ endif ================================================ FILE: core/ccache.mk ================================================ # # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # We no longer provide a ccache prebuilt. # # Ours was old, and had a number of issues that triggered non-reproducible # results and other failures. Newer ccache versions may fix some of those # issues, but at the large scale of our build servers, we weren't seeing # significant performance gains from using ccache -- you end up needing very # good locality and/or very large caches if you're building many different # configurations. # # Local no-change full rebuilds were showing better results, but why not just # use incremental builds at that point? # # So if you still want to use ccache, continue setting USE_CCACHE, but also set # the CCACHE_EXEC environment variable to the path to your ccache executable. ifneq ($(CCACHE_EXEC),) ifneq ($(filter-out false,$(USE_CCACHE)),) # The default check uses size and modification time, causing false misses # since the mtime depends when the repo was checked out CCACHE_COMPILERCHECK ?= content # See man page, optimizations to get more cache hits # implies that __DATE__ and __TIME__ are not critical for functionality. # Ignore include file modification time since it will depend on when # the repo was checked out CCACHE_SLOPPINESS := time_macros,include_file_mtime,file_macro # Turn all preprocessor absolute paths into relative paths. # Fixes absolute paths in preprocessed source due to use of -g. # We don't really use system headers much so the rootdir is # fine; ensures these paths are relative for all Android trees # on a workstation. CCACHE_BASEDIR := / # Workaround for ccache with clang. # See http://petereisentraut.blogspot.com/2011/09/ccache-and-clang-part-2.html CCACHE_CPP2 := true ifndef CC_WRAPPER CC_WRAPPER := $(CCACHE_EXEC) endif ifndef CXX_WRAPPER CXX_WRAPPER := $(CCACHE_EXEC) endif endif endif ================================================ FILE: core/check_elf_file.mk ================================================ # Check the correctness of the prebuilt ELF files # # This check ensures that DT_SONAME matches with the filename, DT_NEEDED # matches the shared libraries specified in LOCAL_SHARED_LIBRARIES, and all # undefined symbols in the prebuilt binary can be found in one of the shared # libraries specified in LOCAL_SHARED_LIBRARIES. # # Inputs: # - LOCAL_ALLOW_UNDEFINED_SYMBOLS # - LOCAL_IGNORE_MAX_PAGE_SIZE # - LOCAL_BUILT_MODULE # - LOCAL_IS_HOST_MODULE # - LOCAL_MODULE_CLASS # - TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE # - TARGET_MAX_PAGE_SIZE_SUPPORTED # - intermediates # - my_installed_module_stem # - my_prebuilt_src_file # - my_check_elf_file_shared_lib_files # - my_system_shared_libraries ifndef LOCAL_IS_HOST_MODULE ifneq ($(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS),) check_elf_files_stamp := $(intermediates)/check_elf_files.timestamp $(check_elf_files_stamp): PRIVATE_SONAME := $(if $(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES),$(my_installed_module_stem)) $(check_elf_files_stamp): PRIVATE_ALLOW_UNDEFINED_SYMBOLS := $(LOCAL_ALLOW_UNDEFINED_SYMBOLS) $(check_elf_files_stamp): PRIVATE_SYSTEM_SHARED_LIBRARIES := $(my_system_shared_libraries) # PRIVATE_SHARED_LIBRARY_FILES are file paths to built shared libraries. # In addition to $(my_check_elf_file_shared_lib_files), some file paths are # added by `resolve-shared-libs-for-elf-file-check` from `core/main.mk`. $(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := $(my_check_elf_file_shared_lib_files) # For different page sizes to work, we must support a larger max page size # as well as properly reflect page size at runtime. Limit this check, since many # devices set the max page size (for future proof) than actually use the # larger page size. ifeq ($(strip $(TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE)),true) ifeq ($(strip $(LOCAL_IGNORE_MAX_PAGE_SIZE)),true) $(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE := else $(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE := $(TARGET_MAX_PAGE_SIZE_SUPPORTED) endif else $(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE := endif $(check_elf_files_stamp): $(my_prebuilt_src_file) $(my_check_elf_file_shared_lib_files) $(CHECK_ELF_FILE) $(LLVM_READOBJ) @echo Check prebuilt ELF binary: $< $(hide) mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) $(CHECK_ELF_FILE) \ --skip-bad-elf-magic \ --skip-unknown-elf-machine \ $(if $(PRIVATE_MAX_PAGE_SIZE),--max-page-size=$(PRIVATE_MAX_PAGE_SIZE)) \ $(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \ $(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \ $(foreach l,$(PRIVATE_SYSTEM_SHARED_LIBRARIES),--system-shared-lib $(l)) \ $(if $(PRIVATE_ALLOW_UNDEFINED_SYMBOLS),--allow-undefined-symbols) \ --llvm-readobj=$(LLVM_READOBJ) \ $< $(hide) touch $@ CHECK_ELF_FILES.$(check_elf_files_stamp) := 1 ifneq ($(strip $(LOCAL_CHECK_ELF_FILES)),false) ifneq ($(strip $(BUILD_BROKEN_PREBUILT_ELF_FILES)),true) $(LOCAL_BUILT_MODULE): $(check_elf_files_stamp) check-elf-files: $(check_elf_files_stamp) endif # BUILD_BROKEN_PREBUILT_ELF_FILES endif # LOCAL_CHECK_ELF_FILES endif # SHARED_LIBRARIES, EXECUTABLES, NATIVE_TESTS endif # !LOCAL_IS_HOST_MODULE ================================================ FILE: core/checktree ================================================ #!/usr/bin/python -E import sys, os, re excludes = [r'.*?/\.obj.*?', r'.*?~', r'.*?\/.DS_Store', r'.*?\/.gdb_history', r'.*?\/buildspec.mk', r'.*?/\..*?\.swp', r'.*?/out/.*?', r'.*?/install/.*?'] excludes_compiled = map(re.compile, excludes) def filter_excludes(str): for e in excludes_compiled: if e.match(str): return False return True def split_perforce_parts(s): spaces = ((s.count(" ") + 1) / 3) * 2 pos = 0 while spaces > 0: pos = s.find(" ", pos) + 1 spaces = spaces - 1 return s[pos:] def quotate(s): return '"' + s + '"' class PerforceError(Exception): def __init__(self,value): self.value = value def __str__(self): return repr(self.value) def run(command, regex, filt): def matchit(s): m = regex_compiled.match(s) if m: return m.group(1) else: return "" def filterit(s): if filt_compiled.match(s): return True else: return False fd = os.popen(command); lines = fd.readlines() status = fd.close() if status: raise PerforceError("error calling " + command) regex_compiled = re.compile(regex) filt_compiled = re.compile(filt) if len(lines) >= 1: lines = filter(filterit, lines) if len(lines) >= 1: return map(matchit, lines) return None try: if len(sys.argv) == 1: do_exclude = True elif len(sys.argv) == 2 and sys.argv[1] == "-a": do_exclude = False else: print "usage: checktree [-a]" print " -a don't filter common crud in the tree" sys.exit(1) have = run("p4 have ...", r'[^#]+#[0-9]+ - (.*)', r'.*') cwd = os.getcwd() files = run("find . -not -type d", r'.(.*)', r'.*') files = map(lambda s: cwd+s, files) added_depot_path = run("p4 opened ...", r'([^#]+)#.*', r'.*?#[0-9]+ - add .*'); added = [] if added_depot_path: added_depot_path = map(quotate, added_depot_path) where = "p4 where " + " ".join(added_depot_path) added = run(where, r'(.*)', r'.*') added = map(split_perforce_parts, added) extras = [] # Python 2.3 -- still default on Mac OS X -- does not have set() # Make dict's here to support the "in" operations below have = dict().fromkeys(have, 1) added = dict().fromkeys(added, 1) for file in files: if not file in have: if not file in added: extras.append(file) if do_exclude: extras = filter(filter_excludes, extras) for s in extras: print s.replace(" ", "\\ ") except PerforceError, e: sys.exit(2) ================================================ FILE: core/clang/HOST_x86.mk ================================================ $(clang_2nd_arch_prefix)HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i386.a $(clang_2nd_arch_prefix)HOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i386.a ================================================ FILE: core/clang/HOST_x86_64.mk ================================================ HOST_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64.a HOST_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64.a ================================================ FILE: core/clang/TARGET_arm.mk ================================================ $(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi $(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS := $(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := armv7-none-linux-gnueabi $(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-arm-android.a $(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-arm-android.a $(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/arm/libunwind.a # Address sanitizer clang config $(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan $(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan $(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := arm ================================================ FILE: core/clang/TARGET_arm64.mk ================================================ RS_TRIPLE := renderscript64-linux-android RS_TRIPLE_CFLAGS := RS_COMPAT_TRIPLE := aarch64-linux-android TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-aarch64-android.a TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-aarch64-android.a TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/aarch64/libunwind.a # Address sanitizer clang config ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64 ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64 PREBUILT_LIBCXX_ARCH_DIR := aarch64 ================================================ FILE: core/clang/TARGET_riscv64.mk ================================================ RS_TRIPLE := renderscript64-linux-android RS_TRIPLE_CFLAGS := -D__riscv64__ RS_COMPAT_TRIPLE := riscv64-linux-android TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-riscv64-android.a TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-riscv64-android.a TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/riscv64/libunwind.a # Address sanitizer clang config ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64 ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64 PREBUILT_LIBCXX_ARCH_DIR := riscv64 ================================================ FILE: core/clang/TARGET_x86.mk ================================================ $(clang_2nd_arch_prefix)RS_TRIPLE := renderscript32-linux-androideabi $(clang_2nd_arch_prefix)RS_TRIPLE_CFLAGS := -D__i386__ $(clang_2nd_arch_prefix)RS_COMPAT_TRIPLE := i686-linux-android $(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i686-android.a $(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i686-android.a $(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/i386/libunwind.a # Address sanitizer clang config $(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan $(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan $(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := i386 ================================================ FILE: core/clang/TARGET_x86_64.mk ================================================ RS_TRIPLE := renderscript64-linux-android RS_TRIPLE_CFLAGS := -D__x86_64__ RS_COMPAT_TRIPLE := x86_64-linux-android TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64-android.a TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64-android.a TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/x86_64/libunwind.a # Address sanitizer clang config ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64 ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64 PREBUILT_LIBCXX_ARCH_DIR := x86_64 ================================================ FILE: core/clang/config.mk ================================================ ## Clang configurations. LLVM_READOBJ := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-readobj LLVM_RTLIB_PATH := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/clang/$(LLVM_RELEASE_VERSION)/lib/linux/ define convert-to-clang-flags $(strip $(filter-out $(CLANG_CONFIG_UNKNOWN_CFLAGS),$(1))) endef CLANG_DEFAULT_UB_CHECKS := \ bool \ integer-divide-by-zero \ return \ returns-nonnull-attribute \ shift-exponent \ unreachable \ vla-bound \ # TODO(danalbert): The following checks currently have compiler performance # issues. # CLANG_DEFAULT_UB_CHECKS += alignment # CLANG_DEFAULT_UB_CHECKS += bounds # CLANG_DEFAULT_UB_CHECKS += enum # CLANG_DEFAULT_UB_CHECKS += float-cast-overflow # CLANG_DEFAULT_UB_CHECKS += float-divide-by-zero # CLANG_DEFAULT_UB_CHECKS += nonnull-attribute # CLANG_DEFAULT_UB_CHECKS += null # CLANG_DEFAULT_UB_CHECKS += shift-base # CLANG_DEFAULT_UB_CHECKS += signed-integer-overflow # TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on. # https://llvm.org/PR19302 # http://reviews.llvm.org/D6974 # CLANG_DEFAULT_UB_CHECKS += object-size # HOST config clang_2nd_arch_prefix := include $(BUILD_SYSTEM)/clang/HOST_$(HOST_ARCH).mk # HOST_2ND_ARCH config ifdef HOST_2ND_ARCH clang_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/clang/HOST_$(HOST_2ND_ARCH).mk endif # TARGET config clang_2nd_arch_prefix := include $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_ARCH).mk # TARGET_2ND_ARCH config ifdef TARGET_2ND_ARCH clang_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/clang/TARGET_$(TARGET_2ND_ARCH).mk endif include $(BUILD_SYSTEM)/clang/tidy.mk ================================================ FILE: core/clang/tidy.mk ================================================ # # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Returns 2nd word of $(1) if $(2) has prefix of the 1st word of $(1). define find_default_local_tidy_check2 $(if $(filter $(word 1,$(1))%,$(2)/),$(word 2,$(1))) endef # Returns 2nd part of $(1) if $(2) has prefix of the 1st part of $(1). define find_default_local_tidy_check $(call find_default_local_tidy_check2,$(subst :,$(space),$(1)),$(2)) endef # Returns the default tidy check list for local project path $(1). # Match $(1) with all patterns in DEFAULT_LOCAL_TIDY_CHECKS and use the last # most specific pattern. define default_global_tidy_checks $(lastword \ $(DEFAULT_GLOBAL_TIDY_CHECKS) \ $(foreach pattern,$(DEFAULT_LOCAL_TIDY_CHECKS), \ $(call find_default_local_tidy_check,$(pattern),$(1)) \ ) \ ) endef # Default filter contains current directory $1 and optional DEFAULT_TIDY_HEADER_DIRS. define default_tidy_header_filter -header-filter=$(if $(DEFAULT_TIDY_HEADER_DIRS),"($1/|$(DEFAULT_TIDY_HEADER_DIRS))",$1/) endef ================================================ FILE: core/cleanbuild.mk ================================================ # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Absolute path of the present working direcotry. # This overrides the shell variable $PWD, which does not necessarily points to # the top of the source tree, for example when "make -C" is used in m/mm/mmm. PWD := $(shell pwd) TOP := . TOPDIR := BUILD_SYSTEM := $(TOPDIR)build/make/core # Set up various standard variables based on configuration # and host information. include $(BUILD_SYSTEM)/config.mk include $(SOONG_MAKEVARS_MK) include $(BUILD_SYSTEM)/clang/config.mk # CTS-specific config. -include cts/build/config.mk # device-tests-specific-config. -include tools/tradefederation/build/suites/device-tests/config.mk # general-tests-specific-config. -include tools/tradefederation/build/suites/general-tests/config.mk INTERNAL_CLEAN_STEPS := # Builds up a list of clean steps. Creates a unique # id for each step by taking makefile path, INTERNAL_CLEAN_BUILD_VERSION # and appending an increasing number of '@' characters. # # $(1): shell command to run # $(2): indicate to not use makefile path as part of step id if not empty. # $(2) should only be used in build/make/core/cleanspec.mk: just for compatibility. define _add-clean-step $(if $(strip $(INTERNAL_CLEAN_BUILD_VERSION)),, \ $(error INTERNAL_CLEAN_BUILD_VERSION not set)) $(eval _acs_makefile_prefix := $(lastword $(MAKEFILE_LIST))) $(eval _acs_makefile_prefix := $(subst /,_,$(_acs_makefile_prefix))) $(eval _acs_makefile_prefix := $(subst .,-,$(_acs_makefile_prefix))) $(eval _acs_makefile_prefix := $(_acs_makefile_prefix)_acs) $(if $($(_acs_makefile_prefix)),,\ $(eval $(_acs_makefile_prefix) := $(INTERNAL_CLEAN_BUILD_VERSION))) $(eval $(_acs_makefile_prefix) := $($(_acs_makefile_prefix))@) $(if $(strip $(2)),$(eval _acs_id := $($(_acs_makefile_prefix))),\ $(eval _acs_id := $(_acs_makefile_prefix)$($(_acs_makefile_prefix)))) $(eval INTERNAL_CLEAN_STEPS += $(_acs_id)) $(eval INTERNAL_CLEAN_STEP.$(_acs_id) := $(1)) $(eval _acs_id :=) $(eval _acs_makefile_prefix :=) endef define add-clean-step $(eval # for build/make/core/cleanspec.mk, dont use makefile path as part of step id) \ $(if $(filter %/cleanspec.mk,$(lastword $(MAKEFILE_LIST))),\ $(eval $(call _add-clean-step,$(1),true)),\ $(eval $(call _add-clean-step,$(1)))) endef # Defines INTERNAL_CLEAN_BUILD_VERSION and the individual clean steps. # cleanspec.mk is outside of the core directory so that more people # can have permission to touch it. include $(BUILD_SYSTEM)/cleanspec.mk INTERNAL_CLEAN_BUILD_VERSION := $(strip $(INTERNAL_CLEAN_BUILD_VERSION)) INTERNAL_CLEAN_STEPS := $(strip $(INTERNAL_CLEAN_STEPS)) # If the clean_steps.mk file is missing (usually after a clean build) # then we won't do anything. CURRENT_CLEAN_BUILD_VERSION := MISSING CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS) # Read the current state from the file, if present. # Will set CURRENT_CLEAN_BUILD_VERSION and CURRENT_CLEAN_STEPS. # clean_steps_file := $(PRODUCT_OUT)/clean_steps.mk -include $(clean_steps_file) ifeq ($(CURRENT_CLEAN_BUILD_VERSION),MISSING) # Do nothing else ifneq ($(CURRENT_CLEAN_BUILD_VERSION),$(INTERNAL_CLEAN_BUILD_VERSION)) # The major clean version is out-of-date. Do a full clean, and # don't even bother with the clean steps. $(info *** A clean build is required because of a recent change.) $(shell rm -rf $(OUT_DIR)) $(info *** Done with the cleaning, now starting the real build.) else # The major clean version is correct. Find the list of clean steps # that we need to execute to get up-to-date. steps := \ $(filter-out $(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_STEPS)) $(foreach step,$(steps), \ $(info Clean step: $(INTERNAL_CLEAN_STEP.$(step))) \ $(shell $(INTERNAL_CLEAN_STEP.$(step))) \ ) # Rewrite the clean step for the second arch. ifdef TARGET_2ND_ARCH # $(1): the clean step cmd # $(2): the prefix to search for # $(3): the prefix to replace with define -cs-rewrite-cleanstep $(if $(filter $(2)/%,$(1)),\ $(eval _crs_new_cmd := $(patsubst $(2)/%,$(3)/%,$(1)))\ $(info Clean step: $(_crs_new_cmd))\ $(shell $(_crs_new_cmd))) endef $(foreach step,$(steps), \ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_INTERMEDIATES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES))\ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES))\ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES))\ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES),$(TARGET_OUT_INTERMEDIATES))\ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES),$(TARGET_OUT_SHARED_LIBRARIES))\ $(call -cs-rewrite-cleanstep,$(INTERNAL_CLEAN_STEP.$(step)),$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES),$(TARGET_OUT_VENDOR_SHARED_LIBRARIES))\ ) endif _crs_new_cmd := steps := endif # Write the new state to the file. # ifneq ($(CURRENT_CLEAN_BUILD_VERSION)-$(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_BUILD_VERSION)-$(INTERNAL_CLEAN_STEPS)) $(shell mkdir -p $(dir $(clean_steps_file))) $(file >$(clean_steps_file).tmp,CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)$(newline)CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)$(newline)) $(shell if ! cmp -s $(clean_steps_file).tmp $(clean_steps_file); then \ mv $(clean_steps_file).tmp $(clean_steps_file); \ else \ rm $(clean_steps_file).tmp; \ fi) endif CURRENT_CLEAN_BUILD_VERSION := CURRENT_CLEAN_STEPS := clean_steps_file := INTERNAL_CLEAN_STEPS := INTERNAL_CLEAN_BUILD_VERSION := ================================================ FILE: core/cleanspec.mk ================================================ # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Just bump this if you want to force a clean build. # ********************************************************************** # WHEN DOING SO # 1. DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP IN THIS FILE. # 2. REMOVE ALL FILES NAMED CleanSpec.mk. # 3. BUMP THE VERSION. # IDEALLY, THOSE STEPS SHOULD BE DONE ATOMICALLY. # ********************************************************************** # INTERNAL_CLEAN_BUILD_VERSION := 6 # # *********************************************************************** # Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step! # *********************************************************************** # If you don't need to do a full clean build but would like to touch # a file or delete some intermediate files, add a clean step to the end # of the list. These steps will only be run once, if they haven't been # run before. # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) # $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) # # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with # files that are missing or have been moved. # # Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. # Use $(OUT_DIR) to refer to the "out" directory. # # If you need to re-do something that's already mentioned, just copy # the command and add it to the bottom of the list. E.g., if a change # that you made last week required touching a file and a change you # made today requires touching the same file, just copy the old # touch step and add it to the end of the list. # # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ # For example: #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) $(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-vendor_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-odm_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/obj/ETC/build_manifest-product_intermediates) $(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/etc/security/fsverity) $(call add-clean-step, rm -rf $(TARGET_OUT_ODM)/etc/security/fsverity) $(call add-clean-step, rm -rf $(TARGET_OUT_PRODUCT)/etc/security/fsverity) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ subdir_cleanspecs := \ $(file <$(OUT_DIR)/.module_paths/CleanSpec.mk.list) include $(subdir_cleanspecs) subdir_cleanspecs := ================================================ FILE: core/clear_vars.mk ================================================ ########################################################### ## Clear out values of all variables used by rule templates. ########################################################### # '',true LOCAL_2ND_ARCH_VAR_PREFIX:= LOCAL_32_BIT_ONLY:= LOCAL_AAPT_FLAGS:= LOCAL_AAPT_INCLUDE_ALL_RESOURCES:= LOCAL_AAPT_NAMESPACES:= LOCAL_ACONFIG_FILES:= LOCAL_ADDITIONAL_CERTIFICATES:= LOCAL_ADDITIONAL_CHECKED_MODULE:= LOCAL_ADDITIONAL_DEPENDENCIES:= LOCAL_AIDL_INCLUDES:= LOCAL_ALLOW_UNDEFINED_SYMBOLS:= LOCAL_ANNOTATION_PROCESSORS:= LOCAL_ANNOTATION_PROCESSOR_CLASSES:= LOCAL_APEX_KEY_PATH:= LOCAL_APK_LIBRARIES:= LOCAL_APK_SET_INSTALL_FILE:= LOCAL_APKCERTS_FILE:= LOCAL_ARM_MODE:= LOCAL_ASFLAGS:= LOCAL_ASSET_DIR:= LOCAL_BUILT_MODULE:= LOCAL_BUILT_MODULE_STEM:= LOCAL_CC:= LOCAL_CERTIFICATE:= LOCAL_CFLAGS:= LOCAL_CHECK_SAME_VNDK_VARIANTS:= LOCAL_CHECKED_MODULE:= LOCAL_C_INCLUDES:= LOCAL_CLANG:= LOCAL_CLANG_ASFLAGS:= LOCAL_CLANG_CFLAGS:= LOCAL_CLANG_CONLYFLAGS:= LOCAL_CLANG_CPPFLAGS:= LOCAL_CLANG_LDFLAGS:= LOCAL_CLASSPATH:= LOCAL_COMPATIBILITY_SUITE:= LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY:= LOCAL_COMPATIBILITY_SUPPORT_FILES:= LOCAL_COMPRESSED_MODULE:= LOCAL_CONLYFLAGS:= LOCAL_COPY_HEADERS:= LOCAL_COPY_HEADERS_TO:= LOCAL_CPP_EXTENSION:= LOCAL_CPPFLAGS:= LOCAL_CPP_STD:= LOCAL_C_STD:= LOCAL_CXX:= LOCAL_CXX_STL := default LOCAL_DEX_PREOPT_APP_IMAGE:= LOCAL_DEX_PREOPT_FLAGS:= LOCAL_DEX_PREOPT_GENERATE_PROFILE:= LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING:= LOCAL_DEX_PREOPT:= # '',true,false LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG:= LOCAL_DISABLE_TEST_CONFIG:= LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES:= LOCAL_DONT_CHECK_MODULE:= # Don't delete the META_INF dir when merging static Java libraries. LOCAL_DONT_DELETE_JAR_META_INF:= LOCAL_DONT_MERGE_MANIFESTS:= LOCAL_DPI_FILE_STEM:= LOCAL_DPI_VARIANTS:= LOCAL_DROIDDOC_ANNOTATIONS_ZIP := LOCAL_DROIDDOC_API_VERSIONS_XML := LOCAL_DROIDDOC_DOC_ZIP := LOCAL_DROIDDOC_METADATA_ZIP:= LOCAL_DROIDDOC_STUBS_SRCJAR := LOCAL_DX_FLAGS:= LOCAL_DYLIB_LIBRARIES:= LOCAL_EMMA_INSTRUMENT:= LOCAL_ENFORCE_USES_LIBRARIES:= LOCAL_ERROR_PRONE_FLAGS:= LOCAL_EXPORT_CFLAGS:= LOCAL_EXPORT_C_INCLUDE_DEPS:= LOCAL_EXPORT_C_INCLUDE_DIRS:= LOCAL_EXPORT_HEADER_LIBRARY_HEADERS:= LOCAL_EXPORT_PACKAGE_RESOURCES:= LOCAL_EXPORT_PROGUARD_FLAG_FILES:= LOCAL_EXPORT_SDK_LIBRARIES:= LOCAL_EXPORT_SHARED_LIBRARY_HEADERS:= LOCAL_EXPORT_STATIC_LIBRARY_HEADERS:= LOCAL_EXTRA_FULL_TEST_CONFIGS:= LOCAL_EXTRACT_APK:= LOCAL_EXTRACT_DPI_APK:= LOCAL_FILESYSTEM_FILELIST:= LOCAL_FINDBUGS_FLAGS:= LOCAL_FORCE_STATIC_EXECUTABLE:= LOCAL_FULL_CLASSES_JACOCO_JAR:= LOCAL_FULL_CLASSES_PRE_JACOCO_JAR:= LOCAL_FULL_INIT_RC:= LOCAL_FULL_LIBS_MANIFEST_FILES:= LOCAL_FULL_MANIFEST_FILE:= LOCAL_FULL_TEST_CONFIG:= LOCAL_FULL_VINTF_FRAGMENTS:= LOCAL_FUZZ_ENGINE:= LOCAL_FUZZ_INSTALLED_SHARED_DEPS:= LOCAL_GCNO_FILES:= LOCAL_GENERATED_SOURCES:= # Group static libraries with "-Wl,--start-group" and "-Wl,--end-group" when linking. LOCAL_GROUP_STATIC_LIBRARIES:= LOCAL_GTEST:=true LOCAL_HEADER_LIBRARIES:= LOCAL_HOST_PREFIX:= LOCAL_HOST_REQUIRED_MODULES:= LOCAL_IGNORE_MAX_PAGE_SIZE:= LOCAL_INIT_RC:= LOCAL_INJECT_BSSL_HASH:= LOCAL_INSTALLED_MODULE:= LOCAL_INSTALLED_MODULE_STEM:= LOCAL_INSTRUMENTATION_FOR:= LOCAL_INTERMEDIATE_SOURCES:= LOCAL_INTERMEDIATE_TARGETS:= LOCAL_IS_FUZZ_TARGET:= LOCAL_IS_HOST_MODULE:= LOCAL_IS_RUNTIME_RESOURCE_OVERLAY:= LOCAL_IS_UNIT_TEST:= LOCAL_TEST_OPTIONS_TAGS:= LOCAL_JACK_COVERAGE_EXCLUDE_FILTER:= LOCAL_JACK_COVERAGE_INCLUDE_FILTER:= LOCAL_JAR_EXCLUDE_FILES:= LOCAL_JAR_EXCLUDE_PACKAGES:= LOCAL_JARJAR_RULES:= LOCAL_JAR_MANIFEST:= LOCAL_JAR_PACKAGES:= LOCAL_JAR_PROCESSOR:= LOCAL_JAR_PROCESSOR_ARGS:= LOCAL_JAVACFLAGS:= LOCAL_JAVA_LANGUAGE_VERSION:= LOCAL_JAVA_LIBRARIES:= LOCAL_JAVA_RESOURCE_DIRS:= LOCAL_JAVA_RESOURCE_FILES:= LOCAL_JNI_SHARED_LIBRARIES:= LOCAL_JNI_SHARED_LIBRARIES_ABI:= LOCAL_CERTIFICATE_LINEAGE:= LOCAL_LDFLAGS:= LOCAL_LDLIBS:= LOCAL_LICENSE_CONDITIONS:= LOCAL_LICENSE_KINDS:= LOCAL_LICENSE_INSTALL_MAP:= LOCAL_LICENSE_PACKAGE_NAME:= LOCAL_LOGTAGS_FILES:= LOCAL_MANIFEST_FILE:= LOCAL_MANIFEST_INSTRUMENTATION_FOR:= LOCAL_MANIFEST_PACKAGE_NAME:= LOCAL_MIN_SDK_VERSION:= LOCAL_MODULE:= LOCAL_MODULE_CLASS:= LOCAL_MODULE_HOST_ARCH:= LOCAL_MODULE_HOST_ARCH_WARN:= LOCAL_MODULE_HOST_CROSS_ARCH:= LOCAL_MODULE_HOST_OS:= LOCAL_MODULE_IS_CONTAINER:= LOCAL_MODULE_OWNER:= LOCAL_MODULE_PATH:= LOCAL_MODULE_RELATIVE_PATH := LOCAL_MODULE_STEM:= LOCAL_MODULE_SUFFIX:= LOCAL_MODULE_SYMLINKS:= LOCAL_MODULE_TAGS:= LOCAL_MODULE_TARGET_ARCH:= LOCAL_MODULE_TARGET_ARCH_WARN:= LOCAL_MODULE_TYPE:= LOCAL_MODULE_UNSUPPORTED_HOST_ARCH:= LOCAL_MODULE_UNSUPPORTED_HOST_ARCH_WARN:= LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH:= LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN:= LOCAL_MULTILIB:= LOCAL_NATIVE_BENCHMARK:= LOCAL_NDK_STL_VARIANT:= LOCAL_NDK_VERSION:=current LOCAL_NO_CRT:= LOCAL_NO_DEFAULT_COMPILER_FLAGS:= LOCAL_NO_LIBCRT_BUILTINS:= LOCAL_NO_NOTICE_FILE:= LOCAL_NOSANITIZE:= LOCAL_NO_STANDARD_LIBRARIES:= LOCAL_NO_STATIC_ANALYZER:= LOCAL_NOT_AVAILABLE_FOR_PLATFORM:= LOCAL_NOTICE_FILE:= LOCAL_ODM_MODULE:= LOCAL_OEM_MODULE:= LOCAL_OPTIONAL_USES_LIBRARIES:= LOCAL_OVERRIDES_PACKAGES:= LOCAL_OVERRIDES_MODULES:= LOCAL_PACKAGE_NAME:= LOCAL_PACKAGE_SPLITS:= LOCAL_PACK_MODULE_RELOCATIONS:= LOCAL_PATCH_MODULE:= LOCAL_PICKUP_FILES:= LOCAL_POST_INSTALL_CMD:= LOCAL_POST_LINK_CMD:= LOCAL_PREBUILT_COVERAGE_ARCHIVE:= LOCAL_PREBUILT_EXECUTABLES:= LOCAL_PREBUILT_JAVA_LIBRARIES:= LOCAL_PREBUILT_JNI_LIBS:= LOCAL_PREBUILT_LIBS:= LOCAL_PREBUILT_MODULE_FILE:= LOCAL_PREBUILT_OBJ_FILES:= LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:= LOCAL_USE_EMBEDDED_DEX:= LOCAL_USE_EMBEDDED_NATIVE_LIBS:= LOCAL_PRIVATE_PLATFORM_APIS:= LOCAL_PRIVILEGED_MODULE:= LOCAL_PROC_MACRO_LIBRARIES:= # '',full,custom,disabled,obfuscation,optimization LOCAL_PRODUCT_MODULE:= # TODO(b/135957588) Remove LOCAL_PRODUCT_SERVICES_MODULE LOCAL_PRODUCT_SERVICES_MODULE := LOCAL_PROGUARD_ENABLED:= LOCAL_PROGUARD_FLAG_FILES:= LOCAL_PROGUARD_FLAGS:= LOCAL_PROGUARD_FLAGS_DEPS:= LOCAL_PROPRIETARY_MODULE:= LOCAL_PROTOC_FLAGS:= # lite(default),micro,nano,stream,full,nanopb-c,nanopb-c-enable_malloc,nanopb-c-16bit,nanopb-c-enable_malloc-16bit,nanopb-c-32bit,nanopb-c-enable_malloc-32bit LOCAL_PROTOC_OPTIMIZE_TYPE:= LOCAL_PROTO_JAVA_OUTPUT_PARAMS:= LOCAL_PROVIDES_USES_LIBRARY:= LOCAL_R8_FLAG_FILES:= LOCAL_RECORDED_MODULE_TYPE:= LOCAL_RENDERSCRIPT_CC:= LOCAL_RENDERSCRIPT_COMPATIBILITY:= LOCAL_RENDERSCRIPT_FLAGS:= LOCAL_RENDERSCRIPT_INCLUDES:= LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE:= LOCAL_RENDERSCRIPT_TARGET_API:= # Used to replace the installed file of a presigned prebuilt apk in PDK fusion build, # to avoid installing the presigned apks with classes.dex unstripped. LOCAL_REPLACE_PREBUILT_APK_INSTALLED:= LOCAL_REQUIRED_MODULES:= LOCAL_RES_LIBRARIES:= LOCAL_RESOURCE_DIR:= LOCAL_RLIB_LIBRARIES:= LOCAL_ROTATION_MIN_SDK_VERSION:= LOCAL_RUNTIME_LIBRARIES:= LOCAL_RRO_THEME:= LOCAL_RTTI_FLAG:= LOCAL_SANITIZE:= LOCAL_SANITIZE_DIAG:= LOCAL_SANITIZE_RECOVER:= LOCAL_SANITIZE_NO_RECOVER:= LOCAL_SANITIZE_BLOCKLIST := LOCAL_SDK_LIBRARIES := LOCAL_SDK_RES_VERSION:= LOCAL_SDK_VERSION:= LOCAL_SHARED_ANDROID_LIBRARIES:= LOCAL_SHARED_LIBRARIES:= LOCAL_SOONG_AAR := LOCAL_SOONG_BUILT_INSTALLED := LOCAL_SOONG_BUNDLE := LOCAL_SOONG_CLASSES_JAR := LOCAL_SOONG_DEX_JAR := LOCAL_SOONG_DEXPREOPT_CONFIG := LOCAL_SOONG_EXPORT_PROGUARD_FLAGS := LOCAL_SOONG_HEADER_JAR := LOCAL_SOONG_INSTALL_PAIRS := LOCAL_SOONG_INSTALL_SYMLINKS := LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES:= LOCAL_SOONG_INSTALLED_MODULE := LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR := LOCAL_SOONG_LICENSE_METADATA := LOCAL_SOONG_LINK_TYPE := LOCAL_SOONG_LINT_REPORTS := LOCAL_SOONG_LOGTAGS_FILES := LOCAL_SOONG_MODULE_INFO_JSON := LOCAL_SOONG_MODULE_TYPE := LOCAL_SOONG_PROGUARD_DICT := LOCAL_SOONG_PROGUARD_USAGE_ZIP := LOCAL_SOONG_PROVIDER_TEST_SUITES := LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE := LOCAL_SOONG_TRANSITIVE_RES_PACKAGES := LOCAL_SOONG_DEVICE_RRO_DIRS := LOCAL_SOONG_PRODUCT_RRO_DIRS := LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES := LOCAL_SOONG_SYMBOL_PATH := LOCAL_SOONG_TOC := LOCAL_SOONG_UNSTRIPPED_BINARY := LOCAL_SOONG_VNDK_VERSION := # '',true LOCAL_SOURCE_FILES_ALL_GENERATED:= LOCAL_SRC_FILES:= LOCAL_SRC_FILES_EXCLUDE:= LOCAL_SRCJARS:= LOCAL_STATIC_ANDROID_LIBRARIES:= LOCAL_STATIC_JAVA_AAR_LIBRARIES:= LOCAL_STATIC_JAVA_LIBRARIES:= LOCAL_STATIC_LIBRARIES:= LOCAL_SYSTEM_EXT_MODULE:= LOCAL_STRIP_MODULE:= LOCAL_SYSTEM_SHARED_LIBRARIES:=none LOCAL_TARGET_REQUIRED_MODULES:= LOCAL_TEST_CONFIG:= LOCAL_TEST_CONFIG_SUFFIX:= LOCAL_TEST_DATA:= LOCAL_TEST_DATA_BINS:= LOCAL_TEST_MAINLINE_MODULES:= LOCAL_TEST_MODULE_TO_PROGUARD_WITH:= LOCAL_TEST_MODULE_CONFIG_BASE:= LOCAL_TIDY:= LOCAL_TIDY_CHECKS:= LOCAL_TIDY_FLAGS:= LOCAL_UNCOMPRESS_DEX:= LOCAL_UNINSTALLABLE_MODULE:= LOCAL_UNSTRIPPED_PATH:= LOCAL_USE_AAPT2:= LOCAL_USE_CLANG_LLD:= LOCAL_USE_VNDK:= LOCAL_IN_VENDOR:= LOCAL_IN_PRODUCT:= LOCAL_USES_LIBRARIES:= LOCAL_VENDOR_MODULE:= LOCAL_VINTF_FRAGMENTS:= LOCAL_VNDK_DEPEND_ON_CORE_VARIANT:= LOCAL_WARNINGS_ENABLE:= LOCAL_WHOLE_STATIC_LIBRARIES:= LOCAL_YACCFLAGS:= LOCAL_CHECK_ELF_FILES:= # arch specific variables LOCAL_ASFLAGS_$(TARGET_ARCH):= LOCAL_CFLAGS_$(TARGET_ARCH):= LOCAL_C_INCLUDES_$(TARGET_ARCH):= LOCAL_CLANG_ASFLAGS_$(TARGET_ARCH):= LOCAL_CLANG_CFLAGS_$(TARGET_ARCH):= LOCAL_CLANG_CPPFLAGS_$(TARGET_ARCH):= LOCAL_CLANG_LDFLAGS_$(TARGET_ARCH):= LOCAL_CLANG_$(TARGET_ARCH):= LOCAL_CPPFLAGS_$(TARGET_ARCH):= LOCAL_GENERATED_SOURCES_$(TARGET_ARCH):= LOCAL_HEADER_LIBRARIES_$(TARGET_ARCH):= LOCAL_LDFLAGS_$(TARGET_ARCH):= LOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_ARCH):= LOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH):= LOCAL_REQUIRED_MODULES_$(TARGET_ARCH):= LOCAL_RUNTIME_LIBRARIES_$(TARGET_ARCH):= LOCAL_SHARED_LIBRARIES_$(TARGET_ARCH):= LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH):= LOCAL_SOONG_JNI_LIBS_SYMBOLS:= LOCAL_SRC_FILES_EXCLUDE_$(TARGET_ARCH):= LOCAL_SRC_FILES_$(TARGET_ARCH):= LOCAL_STATIC_LIBRARIES_$(TARGET_ARCH):= LOCAL_STRIP_MODULE_$(TARGET_ARCH):= LOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_ARCH):= ifdef TARGET_2ND_ARCH LOCAL_ASFLAGS_$(TARGET_2ND_ARCH):= LOCAL_CFLAGS_$(TARGET_2ND_ARCH):= LOCAL_C_INCLUDES_$(TARGET_2ND_ARCH):= LOCAL_CLANG_ASFLAGS_$(TARGET_2ND_ARCH):= LOCAL_CLANG_CFLAGS_$(TARGET_2ND_ARCH):= LOCAL_CLANG_CPPFLAGS_$(TARGET_2ND_ARCH):= LOCAL_CLANG_LDFLAGS_$(TARGET_2ND_ARCH):= LOCAL_CLANG_$(TARGET_2ND_ARCH):= LOCAL_CPPFLAGS_$(TARGET_2ND_ARCH):= LOCAL_GENERATED_SOURCES_$(TARGET_2ND_ARCH):= LOCAL_HEADER_LIBRARIES_$(TARGET_2ND_ARCH):= LOCAL_LDFLAGS_$(TARGET_2ND_ARCH):= LOCAL_PACK_MODULE_RELOCATIONS_$(TARGET_2ND_ARCH):= LOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH):= LOCAL_REQUIRED_MODULES_$(TARGET_2ND_ARCH):= LOCAL_RUNTIME_LIBRARIES_$(TARGET_2ND_ARCH):= LOCAL_SHARED_LIBRARIES_$(TARGET_2ND_ARCH):= LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH):= LOCAL_SRC_FILES_EXCLUDE_$(TARGET_2ND_ARCH):= LOCAL_SRC_FILES_$(TARGET_2ND_ARCH):= LOCAL_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):= LOCAL_STRIP_MODULE_$(TARGET_2ND_ARCH):= LOCAL_WHOLE_STATIC_LIBRARIES_$(TARGET_2ND_ARCH):= endif LOCAL_ASFLAGS_$(HOST_ARCH):= LOCAL_CFLAGS_$(HOST_ARCH):= LOCAL_C_INCLUDES_$(HOST_ARCH):= LOCAL_CLANG_ASFLAGS_$(HOST_ARCH):= LOCAL_CLANG_CFLAGS_$(HOST_ARCH):= LOCAL_CLANG_CPPFLAGS_$(HOST_ARCH):= LOCAL_CLANG_$(HOST_ARCH):= LOCAL_CLANG_LDFLAGS_$(HOST_ARCH):= LOCAL_CPPFLAGS_$(HOST_ARCH):= LOCAL_GENERATED_SOURCES_$(HOST_ARCH):= LOCAL_HEADER_LIBRARIES_$(HOST_ARCH):= LOCAL_LDFLAGS_$(HOST_ARCH):= LOCAL_REQUIRED_MODULES_$(HOST_ARCH):= LOCAL_RUNTIME_LIBRARIES_$(HOST_ARCH):= LOCAL_SHARED_LIBRARIES_$(HOST_ARCH):= LOCAL_SRC_FILES_EXCLUDE_$(HOST_ARCH):= LOCAL_SRC_FILES_$(HOST_ARCH):= LOCAL_STATIC_LIBRARIES_$(HOST_ARCH):= LOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_ARCH):= ifdef HOST_2ND_ARCH LOCAL_ASFLAGS_$(HOST_2ND_ARCH):= LOCAL_CFLAGS_$(HOST_2ND_ARCH):= LOCAL_C_INCLUDES_$(HOST_2ND_ARCH):= LOCAL_CLANG_ASFLAGS_$(HOST_2ND_ARCH):= LOCAL_CLANG_CFLAGS_$(HOST_2ND_ARCH):= LOCAL_CLANG_CPPFLAGS_$(HOST_2ND_ARCH):= LOCAL_CLANG_$(HOST_2ND_ARCH):= LOCAL_CLANG_LDFLAGS_$(HOST_2ND_ARCH):= LOCAL_CPPFLAGS_$(HOST_2ND_ARCH):= LOCAL_GENERATED_SOURCES_$(HOST_2ND_ARCH):= LOCAL_HEADER_LIBRARIES_$(HOST_2ND_ARCH):= LOCAL_LDFLAGS_$(HOST_2ND_ARCH):= LOCAL_REQUIRED_MODULES_$(HOST_2ND_ARCH):= LOCAL_RUNTIME_LIBRARIES_$(HOST_2ND_ARCH):= LOCAL_SHARED_LIBRARIES_$(HOST_2ND_ARCH):= LOCAL_SRC_FILES_EXCLUDE_$(HOST_2ND_ARCH):= LOCAL_SRC_FILES_$(HOST_2ND_ARCH):= LOCAL_STATIC_LIBRARIES_$(HOST_2ND_ARCH):= LOCAL_WHOLE_STATIC_LIBRARIES_$(HOST_2ND_ARCH):= endif LOCAL_ASFLAGS_$(HOST_OS):= LOCAL_CFLAGS_$(HOST_OS):= LOCAL_C_INCLUDES_$(HOST_OS):= LOCAL_CPPFLAGS_$(HOST_OS):= LOCAL_GENERATED_SOURCES_$(HOST_OS):= LOCAL_HEADER_LIBRARIES_$(HOST_OS):= LOCAL_LDFLAGS_$(HOST_OS):= LOCAL_LDLIBS_$(HOST_OS):= LOCAL_REQUIRED_MODULES_$(HOST_OS):= LOCAL_RUNTIME_LIBRARIES_$(HOST_OS):= LOCAL_SHARED_LIBRARIES_$(HOST_OS):= LOCAL_SRC_FILES_$(HOST_OS):= LOCAL_STATIC_LIBRARIES_$(HOST_OS):= LOCAL_SRC_FILES_$(HOST_OS)_$(HOST_ARCH):= ifdef HOST_2ND_ARCH LOCAL_SRC_FILES_$(HOST_OS)_$(HOST_2ND_ARCH):= endif LOCAL_ASFLAGS_32:= LOCAL_ASFLAGS_64:= LOCAL_CFLAGS_32:= LOCAL_CFLAGS_64:= LOCAL_C_INCLUDES_32:= LOCAL_C_INCLUDES_64:= LOCAL_CLANG_32:= LOCAL_CLANG_64:= LOCAL_CLANG_ASFLAGS_32:= LOCAL_CLANG_ASFLAGS_64:= LOCAL_CLANG_CFLAGS_32:= LOCAL_CLANG_CFLAGS_64:= LOCAL_CLANG_CPPFLAGS_32:= LOCAL_CLANG_CPPFLAGS_64:= LOCAL_CLANG_LDFLAGS_32:= LOCAL_CLANG_LDFLAGS_64:= LOCAL_CPPFLAGS_32:= LOCAL_CPPFLAGS_64:= LOCAL_GENERATED_SOURCES_32:= LOCAL_GENERATED_SOURCES_64:= LOCAL_HEADER_LIBRARIES_32:= LOCAL_HEADER_LIBRARIES_64:= LOCAL_INIT_RC_32:= LOCAL_INIT_RC_64:= LOCAL_LDFLAGS_32:= LOCAL_LDFLAGS_64:= LOCAL_MODULE_PATH_32:= LOCAL_MODULE_PATH_64:= LOCAL_MODULE_STEM_32:= LOCAL_MODULE_STEM_64:= LOCAL_MODULE_SYMLINKS_32:= LOCAL_MODULE_SYMLINKS_64:= LOCAL_RUNTIME_LIBRARIES_32:= LOCAL_RUNTIME_LIBRARIES_64:= LOCAL_SHARED_LIBRARIES_32:= LOCAL_SHARED_LIBRARIES_64:= LOCAL_SRC_FILES_32:= LOCAL_SRC_FILES_64:= LOCAL_SRC_FILES_EXCLUDE_32:= LOCAL_SRC_FILES_EXCLUDE_64:= LOCAL_STATIC_LIBRARIES_32:= LOCAL_STATIC_LIBRARIES_64:= LOCAL_WHOLE_STATIC_LIBRARIES_32:= LOCAL_WHOLE_STATIC_LIBRARIES_64:= # Robolectric variables LOCAL_INSTRUMENT_SOURCE_DIRS := LOCAL_INSTRUMENT_SRCJARS := LOCAL_ROBOTEST_FAILURE_FATAL := LOCAL_ROBOTEST_FILES := LOCAL_ROBOTEST_TIMEOUT := LOCAL_TEST_PACKAGE := full_android_manifest := non_system_module := module_license_metadata := # Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to # iterate over thousands of entries every time. # Leave the current makefile to make sure we don't break anything # that expects to be able to find the name of the current makefile. MAKEFILE_LIST := $(lastword $(MAKEFILE_LIST)) ================================================ FILE: core/combo/HOST_darwin.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Darwin (Mac OS X). # Included by combo/select.mk define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_macho,$(1),$(2)) endef HOST_GLOBAL_ARFLAGS := cqs HOST_CUSTOM_LD_COMMAND := true define transform-host-o-to-shared-lib-inner $(hide) $(PRIVATE_CXX) \ -dynamiclib -single_module -read_only_relocs suppress \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ ) \ $(PRIVATE_ALL_OBJECTS) \ $(addprefix -force_load , $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(PRIVATE_LDLIBS) \ -o $@ \ -install_name @rpath/$(notdir $@) \ -Wl,-rpath,@loader_path/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \ -Wl,-rpath,@loader_path/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES)) \ $(PRIVATE_LDFLAGS) endef define transform-host-o-to-executable-inner $(hide) $(PRIVATE_CXX) \ $(foreach path,$(PRIVATE_RPATHS), \ -Wl,-rpath,@loader_path/$(path)) \ -o $@ \ -Wl,-headerpad_max_install_names \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ ) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ $(PRIVATE_ALL_OBJECTS) \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(PRIVATE_LDFLAGS) \ $(PRIVATE_LDLIBS) endef ================================================ FILE: core/combo/HOST_linux.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for builds hosted on linux. # Included by combo/select.mk define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef ############################################################ ## Macros after this line are shared by the 64-bit config. ================================================ FILE: core/combo/TARGET_linux-arm.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Linux on ARM. # Included by combo/select.mk # You can set TARGET_ARCH_VARIANT to use an arch version other # than ARMv5TE. Each value should correspond to a file named # $(BUILD_COMBOS)/arch/.mk which must contain # makefile variable definitions. Their # purpose is to allow module Android.mk files to selectively compile # different versions of code based upon the funtionality and # instructions available in a given architecture version. # # The blocks also define specific arch_variant_cflags, which # include defines, and compiler settings for the given architecture # version. # KNOWN_ARMv8_CORES := cortex-a53 cortex-a53.a57 cortex-a55 cortex-a73 cortex-a75 cortex-a76 KNOWN_ARMv8_CORES += kryo kryo385 exynos-m1 exynos-m2 KNOWN_ARMv82a_CORES := cortex-a55 cortex-a75 kryo385 ifeq (,$(strip $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT))) TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT := generic endif # This quickly checks TARGET_2ND_ARCH_VARIANT against the lists above. ifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv82a_CORES))) ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-2a else ifneq (armv8-2a,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-2a instead.) endif else ifneq (,$(filter $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT), $(KNOWN_ARMv8_CORES))) ifeq (,$(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)) TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := armv8-a else ifneq ($(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT),armv8-a) $(error Incorrect TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT, $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT). Use armv8-a instead.) endif endif ifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),) $(error TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT must be set) endif # NEON is mandatory, see: https://developer.android.com/ndk/guides/abis#v7a ARCH_ARM_HAVE_VFP := true ARCH_ARM_HAVE_VFP_D32 := true ARCH_ARM_HAVE_NEON := true define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef $(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true $(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker ================================================ FILE: core/combo/TARGET_linux-arm64.mk ================================================ # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Linux on ARM. # Included by combo/select.mk # You can set TARGET_ARCH_VARIANT to use an arch version other # than ARMv5TE. Each value should correspond to a file named # $(BUILD_COMBOS)/arch/.mk which must contain # makefile variable definitions. Their # purpose is to allow module Android.mk files to selectively compile # different versions of code based upon the funtionality and # instructions available in a given architecture version. # # The blocks also define specific arch_variant_cflags, which # include defines, and compiler settings for the given architecture # version. # ifeq ($(strip $(TARGET_ARCH_VARIANT)),) TARGET_ARCH_VARIANT := armv8 endif define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef TARGET_PACK_MODULE_RELOCATIONS := true TARGET_LINKER := /system/bin/linker64 ================================================ FILE: core/combo/TARGET_linux-riscv64.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Linux on riscv64 as a target. # Included by combo/select.mk # Provide a default variant. ifeq ($(strip $(TARGET_ARCH_VARIANT)),) TARGET_ARCH_VARIANT := riscv64 endif define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef TARGET_LINKER := /system/bin/linker64 ================================================ FILE: core/combo/TARGET_linux-x86.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Linux on x86 as a target. # Included by combo/select.mk # Provide a default variant. ifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT)),) TARGET_$(combo_2nd_arch_prefix)ARCH_VARIANT := x86 endif define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef $(combo_2nd_arch_prefix)TARGET_PACK_MODULE_RELOCATIONS := true $(combo_2nd_arch_prefix)TARGET_LINKER := /system/bin/linker $(combo_2nd_arch_prefix)TARGET_GLOBAL_YASM_FLAGS := -f elf32 -m x86 ================================================ FILE: core/combo/TARGET_linux-x86_64.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Configuration for Linux on x86_64 as a target. # Included by combo/select.mk # Provide a default variant. ifeq ($(strip $(TARGET_ARCH_VARIANT)),) TARGET_ARCH_VARIANT := x86_64 endif define $(combo_var_prefix)transform-shared-lib-to-toc $(call _gen_toc_command_for_elf,$(1),$(2)) endef TARGET_LINKER := /system/bin/linker64 TARGET_GLOBAL_YASM_FLAGS := -f elf64 -m amd64 ================================================ FILE: core/combo/javac.mk ================================================ # Selects a Java compiler. # # Outputs: # ANDROID_JAVA_TOOLCHAIN -- Directory that contains javac and other java tools # ANDROID_COMPILE_WITH_JACK := false ifdef TARGET_BUILD_APPS ifndef TURBINE_ENABLED TURBINE_ENABLED := false endif endif ANDROID_JAVA_TOOLCHAIN := $(ANDROID_JAVA_HOME)/bin # TODO(ccross): remove this, it is needed for now because it is used by # config.mk before makevars from soong are loaded JAVA := $(ANDROID_JAVA_TOOLCHAIN)/java -XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads ================================================ FILE: core/combo/select.mk ================================================ # # Copyright (C) 2006 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Select a combo based on the compiler being used. # # Inputs: # combo_target -- prefix for final variables (HOST_ or TARGET_) # combo_2nd_arch_prefix -- it's defined if this is loaded for the 2nd arch. # ifeq ($(combo_target),HOST_) combo_os_arch := $(HOST_OS) else # Build a target string like "linux-arm" or "darwin-x86". combo_os_arch := $($(combo_target)OS)-$($(combo_target)$(combo_2nd_arch_prefix)ARCH) endif combo_var_prefix := $(combo_2nd_arch_prefix)$(combo_target) # Set reasonable defaults for the various variables ifeq ($(combo_target),HOST_CROSS_) $(KATI_obsolete_var \ $(combo_var_prefix)GLOBAL_ARFLAGS \ $(combo_var_prefix)STATIC_LIB_SUFFIX \ $(combo_var_prefix)transform-shared-lib-to-toc \ ,HOST_CROSS builds are not supported in Make) else $(combo_var_prefix)GLOBAL_ARFLAGS := crsPD --format=gnu $(combo_var_prefix)STATIC_LIB_SUFFIX := .a # Now include the combo for this specific target. include $(BUILD_COMBOS)/$(combo_target)$(combo_os_arch).mk endif ================================================ FILE: core/config.mk ================================================ # This is included by the top-level Makefile. # It sets up standard variables based on the # current configuration and platform, which # are not specific to what is being built. ifndef KATI $(warning Directly using config.mk from make is no longer supported.) $(warning ) $(warning If you are just attempting to build, you probably need to re-source envsetup.sh:) $(warning ) $(warning $$ source build/envsetup.sh) $(warning ) $(warning If you are attempting to emulate get_build_var, use one of the following:) $(warning $$ build/soong/soong_ui.bash --dumpvar-mode) $(warning $$ build/soong/soong_ui.bash --dumpvars-mode) $(warning ) $(error done) endif BUILD_SYSTEM :=$= build/make/core BUILD_SYSTEM_COMMON :=$= build/make/common include $(BUILD_SYSTEM_COMMON)/core.mk # ----------------------------------------------------------------- # Rules and functions to help copy important files to DIST_DIR # when requested. This must be included once only, and must be included before # soong_config (as soong_config calls make_vars-$(TARGET).mk, and soong may # propagate calls to dist-for-goals there). include $(BUILD_SYSTEM)/distdir.mk # Mark variables that should be coming as environment variables from soong_ui # as readonly .KATI_READONLY := OUT_DIR TMPDIR BUILD_DATETIME_FILE ifdef CALLED_FROM_SETUP .KATI_READONLY := CALLED_FROM_SETUP endif ifdef KATI_PACKAGE_MK_DIR .KATI_READONLY := KATI_PACKAGE_MK_DIR endif # Mark variables deprecated/obsolete CHANGES_URL := https://android.googlesource.com/platform/build/+/master/Changes.md .KATI_READONLY := CHANGES_URL $(KATI_deprecated_var TARGET_USES_64_BIT_BINDER,All devices use 64-bit binder by default now. Uses of TARGET_USES_64_BIT_BINDER should be removed.) $(KATI_deprecated_var PRODUCT_SEPOLICY_SPLIT,All devices are built with split sepolicy.) $(KATI_deprecated_var PRODUCT_SEPOLICY_SPLIT_OVERRIDE,All devices are built with split sepolicy.) $(KATI_obsolete_var PATH,Do not use PATH directly. See $(CHANGES_URL)#PATH) $(KATI_obsolete_var PYTHONPATH,Do not use PYTHONPATH directly. See $(CHANGES_URL)#PYTHONPATH) $(KATI_obsolete_var OUT,Use OUT_DIR instead. See $(CHANGES_URL)#OUT) $(KATI_obsolete_var ANDROID_HOST_OUT,Use HOST_OUT instead. See $(CHANGES_URL)#ANDROID_HOST_OUT) $(KATI_obsolete_var ANDROID_PRODUCT_OUT,Use PRODUCT_OUT instead. See $(CHANGES_URL)#ANDROID_PRODUCT_OUT) $(KATI_obsolete_var ANDROID_HOST_OUT_TESTCASES,Use HOST_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_HOST_OUT_TESTCASES) $(KATI_obsolete_var ANDROID_TARGET_OUT_TESTCASES,Use TARGET_OUT_TESTCASES instead. See $(CHANGES_URL)#ANDROID_TARGET_OUT_TESTCASES) $(KATI_obsolete_var ANDROID_BUILD_TOP,Use '.' instead. See $(CHANGES_URL)#ANDROID_BUILD_TOP) $(KATI_obsolete_var \ ANDROID_TOOLCHAIN \ ANDROID_TOOLCHAIN_2ND_ARCH \ ANDROID_DEV_SCRIPTS \ ANDROID_EMULATOR_PREBUILTS \ ANDROID_PRE_BUILD_PATHS \ ,See $(CHANGES_URL)#other_envsetup_variables) $(KATI_obsolete_var PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE,Set FCM Version in device manifest instead. See $(CHANGES_URL)#PRODUCT_COMPATIBILITY_MATRIX_LEVEL_OVERRIDE) $(KATI_obsolete_var USE_CLANG_PLATFORM_BUILD,Clang is the only supported Android compiler. See $(CHANGES_URL)#USE_CLANG_PLATFORM_BUILD) $(KATI_obsolete_var BUILD_DROIDDOC,Droiddoc is only supported in Soong. See details on build/soong/java/droiddoc.go) $(KATI_obsolete_var BUILD_APIDIFF,Apidiff is only supported in Soong. See details on build/soong/java/droiddoc.go) $(KATI_obsolete_var \ DEFAULT_GCC_CPP_STD_VERSION \ HOST_GLOBAL_CFLAGS 2ND_HOST_GLOBAL_CFLAGS \ HOST_GLOBAL_CONLYFLAGS 2ND_HOST_GLOBAL_CONLYFLAGS \ HOST_GLOBAL_CPPFLAGS 2ND_HOST_GLOBAL_CPPFLAGS \ HOST_GLOBAL_LDFLAGS 2ND_HOST_GLOBAL_LDFLAGS \ HOST_GLOBAL_LLDFLAGS 2ND_HOST_GLOBAL_LLDFLAGS \ HOST_CLANG_SUPPORTED 2ND_HOST_CLANG_SUPPORTED \ HOST_CC 2ND_HOST_CC \ HOST_CXX 2ND_HOST_CXX \ HOST_CROSS_GLOBAL_CFLAGS 2ND_HOST_CROSS_GLOBAL_CFLAGS \ HOST_CROSS_GLOBAL_CONLYFLAGS 2ND_HOST_CROSS_GLOBAL_CONLYFLAGS \ HOST_CROSS_GLOBAL_CPPFLAGS 2ND_HOST_CROSS_GLOBAL_CPPFLAGS \ HOST_CROSS_GLOBAL_LDFLAGS 2ND_HOST_CROSS_GLOBAL_LDFLAGS \ HOST_CROSS_GLOBAL_LLDFLAGS 2ND_HOST_CROSS_GLOBAL_LLDFLAGS \ HOST_CROSS_CLANG_SUPPORTED 2ND_HOST_CROSS_CLANG_SUPPORTED \ HOST_CROSS_CC 2ND_HOST_CROSS_CC \ HOST_CROSS_CXX 2ND_HOST_CROSS_CXX \ TARGET_GLOBAL_CFLAGS 2ND_TARGET_GLOBAL_CFLAGS \ TARGET_GLOBAL_CONLYFLAGS 2ND_TARGET_GLOBAL_CONLYFLAGS \ TARGET_GLOBAL_CPPFLAGS 2ND_TARGET_GLOBAL_CPPFLAGS \ TARGET_GLOBAL_LDFLAGS 2ND_TARGET_GLOBAL_LDFLAGS \ TARGET_GLOBAL_LLDFLAGS 2ND_TARGET_GLOBAL_LLDFLAGS \ TARGET_CLANG_SUPPORTED 2ND_TARGET_CLANG_SUPPORTED \ TARGET_CC 2ND_TARGET_CC \ TARGET_CXX 2ND_TARGET_CXX \ TARGET_TOOLCHAIN_ROOT 2ND_TARGET_TOOLCHAIN_ROOT \ HOST_TOOLCHAIN_ROOT 2ND_HOST_TOOLCHAIN_ROOT \ HOST_CROSS_TOOLCHAIN_ROOT 2ND_HOST_CROSS_TOOLCHAIN_ROOT \ HOST_TOOLS_PREFIX 2ND_HOST_TOOLS_PREFIX \ HOST_CROSS_TOOLS_PREFIX 2ND_HOST_CROSS_TOOLS_PREFIX \ HOST_GCC_VERSION 2ND_HOST_GCC_VERSION \ HOST_CROSS_GCC_VERSION 2ND_HOST_CROSS_GCC_VERSION \ TARGET_NDK_GCC_VERSION 2ND_TARGET_NDK_GCC_VERSION \ GLOBAL_CFLAGS_NO_OVERRIDE GLOBAL_CPPFLAGS_NO_OVERRIDE \ ,GCC support has been removed. Use Clang instead) $(KATI_obsolete_var DIST_DIR dist_goal,Use dist-for-goals instead. See $(CHANGES_URL)#dist) $(KATI_obsolete_var TARGET_ANDROID_FILESYSTEM_CONFIG_H,Use TARGET_FS_CONFIG_GEN instead) $(KATI_deprecated_var USER,Use BUILD_USERNAME instead. See $(CHANGES_URL)#USER) $(KATI_obsolete_var TARGET_ROOT_OUT_SBIN,/sbin has been removed, use /system/bin instead) $(KATI_obsolete_var TARGET_ROOT_OUT_SBIN_UNSTRIPPED,/sbin has been removed, use /system/bin instead) $(KATI_obsolete_var BUILD_BROKEN_PHONY_TARGETS) $(KATI_obsolete_var BUILD_BROKEN_DUP_COPY_HEADERS) $(KATI_obsolete_var BUILD_BROKEN_ENG_DEBUG_TAGS) $(KATI_obsolete_export It is a global setting. See $(CHANGES_URL)#export_keyword) $(KATI_obsolete_var BUILD_BROKEN_ANDROIDMK_EXPORTS) $(KATI_obsolete_var PRODUCT_NOTICE_SPLIT_OVERRIDE,Stop using this, keep calm, and carry on.) $(KATI_obsolete_var PRODUCT_STATIC_BOOT_CONTROL_HAL,Use shared library module instead. See $(CHANGES_URL)#PRODUCT_STATIC_BOOT_CONTROL_HAL) $(KATI_obsolete_var \ ARCH_ARM_HAVE_ARMV7A \ ARCH_DSP_REV \ ARCH_HAVE_ALIGNED_DOUBLES \ ARCH_MIPS_HAS_DSP \ ARCH_MIPS_HAS_FPU \ ARCH_MIPS_REV6 \ ARCH_X86_HAVE_AES_NI \ ARCH_X86_HAVE_AVX \ ARCH_X86_HAVE_AVX2 \ ARCH_X86_HAVE_AVX512 \ ARCH_X86_HAVE_MOVBE \ ARCH_X86_HAVE_POPCNT \ ARCH_X86_HAVE_SSE4 \ ARCH_X86_HAVE_SSE4_2 \ ARCH_X86_HAVE_SSSE3 \ ) $(KATI_obsolete_var PRODUCT_IOT) $(KATI_obsolete_var MD5SUM) $(KATI_obsolete_var BOARD_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES) $(KATI_obsolete_var LOCAL_HAL_STATIC_LIBRARIES, See $(CHANGES_URL)#BOARD_HAL_STATIC_LIBRARIES) $(KATI_obsolete_var \ TARGET_AUX_OS_VARIANT_LIST \ LOCAL_AUX_ARCH \ LOCAL_AUX_CPU \ LOCAL_AUX_OS \ LOCAL_AUX_OS_VARIANT \ LOCAL_AUX_SUBARCH \ LOCAL_AUX_TOOLCHAIN \ LOCAL_CUSTOM_BUILD_STEP_INPUT \ LOCAL_CUSTOM_BUILD_STEP_OUTPUT \ LOCAL_IS_AUX_MODULE \ ,AUX support has been removed) $(KATI_obsolete_var HOST_OUT_TEST_CONFIG TARGET_OUT_TEST_CONFIG LOCAL_TEST_CONFIG_OPTIONS) $(KATI_obsolete_var \ TARGET_PROJECT_INCLUDES \ 2ND_TARGET_PROJECT_INCLUDES \ TARGET_PROJECT_SYSTEM_INCLUDES \ 2ND_TARGET_PROJECT_SYSTEM_INCLUDES \ ,Project include variables have been removed) $(KATI_obsolete_var TARGET_PREFER_32_BIT TARGET_PREFER_32_BIT_APPS TARGET_PREFER_32_BIT_EXECUTABLES) $(KATI_obsolete_var PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST) $(KATI_obsolete_var PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST) $(KATI_obsolete_var COVERAGE_PATHS,Use NATIVE_COVERAGE_PATHS instead) $(KATI_obsolete_var COVERAGE_EXCLUDE_PATHS,Use NATIVE_COVERAGE_EXCLUDE_PATHS instead) $(KATI_obsolete_var BOARD_VNDK_RUNTIME_DISABLE,VNDK-Lite is no longer supported) $(KATI_obsolete_var LOCAL_SANITIZE_BLACKLIST,Use LOCAL_SANITIZE_BLOCKLIST instead) $(KATI_obsolete_var BOARD_PLAT_PUBLIC_SEPOLICY_DIR,Use SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS instead) $(KATI_obsolete_var BOARD_PLAT_PRIVATE_SEPOLICY_DIR,Use SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS instead) $(KATI_obsolete_var TARGET_NO_VENDOR_BOOT,Use PRODUCT_BUILD_VENDOR_BOOT_IMAGE instead) $(KATI_obsolete_var PRODUCT_CHECK_ELF_FILES,Use BUILD_BROKEN_PREBUILT_ELF_FILES instead) $(KATI_obsolete_var ALL_GENERATED_SOURCES,ALL_GENERATED_SOURCES is no longer used) $(KATI_obsolete_var ALL_ORIGINAL_DYNAMIC_BINARIES,ALL_ORIGINAL_DYNAMIC_BINARIES is no longer used) $(KATI_obsolete_var PRODUCT_SUPPORTS_VERITY,VB 1.0 and related variables are no longer supported) $(KATI_obsolete_var PRODUCT_SUPPORTS_VERITY_FEC,VB 1.0 and related variables are no longer supported) $(KATI_obsolete_var PRODUCT_SUPPORTS_BOOT_SIGNER,VB 1.0 and related variables are no longer supported) $(KATI_obsolete_var PRODUCT_VERITY_SIGNING_KEY,VB 1.0 and related variables are no longer supported) $(KATI_obsolete_var BOARD_PREBUILT_PVMFWIMAGE,pvmfw.bin is now built in AOSP and custom versions are no longer supported) $(KATI_obsolete_var BUILDING_PVMFW_IMAGE,BUILDING_PVMFW_IMAGE is no longer used) $(KATI_obsolete_var BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(KATI_obsolete_var FS_GET_STATS) $(KATI_obsolete_var BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES) # Used to force goals to build. Only use for conditionally defined goals. .PHONY: FORCE FORCE: ORIGINAL_MAKECMDGOALS := $(MAKECMDGOALS) UNAME := $(shell uname -sm) SRC_TARGET_DIR := $(TOPDIR)build/make/target # Some specific paths to tools SRC_DROIDDOC_DIR := $(TOPDIR)build/make/tools/droiddoc # Mark some inputs as readonly ifdef TARGET_DEVICE_DIR .KATI_READONLY := TARGET_DEVICE_DIR endif ONE_SHOT_MAKEFILE := .KATI_READONLY := ONE_SHOT_MAKEFILE # Set up efficient math functions which are used in make. # Here since this file is included by envsetup as well as during build. include $(BUILD_SYSTEM_COMMON)/math.mk include $(BUILD_SYSTEM_COMMON)/strings.mk include $(BUILD_SYSTEM_COMMON)/json.mk # Various mappings to avoid hard-coding paths all over the place include $(BUILD_SYSTEM)/pathmap.mk # Allow projects to define their own globally-available variables include $(BUILD_SYSTEM)/project_definitions.mk # ############################################################### # Build system internal files # ############################################################### BUILD_COMBOS :=$= $(BUILD_SYSTEM)/combo CLEAR_VARS :=$= $(BUILD_SYSTEM)/clear_vars.mk BUILD_HOST_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/host_static_library.mk BUILD_HOST_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/host_shared_library.mk BUILD_STATIC_LIBRARY :=$= $(BUILD_SYSTEM)/static_library.mk BUILD_HEADER_LIBRARY :=$= $(BUILD_SYSTEM)/header_library.mk BUILD_SHARED_LIBRARY :=$= $(BUILD_SYSTEM)/shared_library.mk BUILD_EXECUTABLE :=$= $(BUILD_SYSTEM)/executable.mk BUILD_HOST_EXECUTABLE :=$= $(BUILD_SYSTEM)/host_executable.mk BUILD_PACKAGE :=$= $(BUILD_SYSTEM)/package.mk BUILD_PHONY_PACKAGE :=$= $(BUILD_SYSTEM)/phony_package.mk BUILD_RRO_PACKAGE :=$= $(BUILD_SYSTEM)/build_rro_package.mk BUILD_HOST_PREBUILT :=$= $(BUILD_SYSTEM)/host_prebuilt.mk BUILD_PREBUILT :=$= $(BUILD_SYSTEM)/prebuilt.mk BUILD_MULTI_PREBUILT :=$= $(BUILD_SYSTEM)/multi_prebuilt.mk BUILD_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/java_library.mk BUILD_STATIC_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/static_java_library.mk BUILD_HOST_JAVA_LIBRARY :=$= $(BUILD_SYSTEM)/host_java_library.mk BUILD_COPY_HEADERS :=$= $(BUILD_SYSTEM)/copy_headers.mk BUILD_NATIVE_TEST :=$= $(BUILD_SYSTEM)/native_test.mk BUILD_FUZZ_TEST :=$= $(BUILD_SYSTEM)/fuzz_test.mk BUILD_NOTICE_FILE :=$= $(BUILD_SYSTEM)/notice_files.mk BUILD_SBOM_GEN :=$= $(BUILD_SYSTEM)/sbom.mk include $(BUILD_SYSTEM)/deprecation.mk # ############################################################### # Parse out any modifier targets. # ############################################################### hide := @ ################################################################ # Tools needed in product configuration makefiles. ################################################################ NORMALIZE_PATH := build/make/tools/normalize_path.py # $(1): the paths to be normalized define normalize-paths $(if $(1),$(shell $(NORMALIZE_PATH) $(1))) endef # ############################################################### # Set common values # ############################################################### # Initialize SOONG_CONFIG_NAMESPACES so that it isn't recursive. SOONG_CONFIG_NAMESPACES := # TODO(asmundak): remove add_soong_config_namespace, add_soong_config_var, # and add_soong_config_var_value once all their usages are replaced with # soong_config_set/soong_config_append. # The add_soong_config_namespace function adds a namespace and initializes it # to be empty. # $1 is the namespace. # Ex: $(call add_soong_config_namespace,acme) define add_soong_config_namespace $(eval SOONG_CONFIG_NAMESPACES += $(strip $1)) \ $(eval SOONG_CONFIG_$(strip $1) :=) endef # The add_soong_config_var function adds a a list of soong config variables to # SOONG_CONFIG_*. The variables and their values are then available to a # soong_config_module_type in an Android.bp file. # $1 is the namespace. $2 is the list of variables. # Ex: $(call add_soong_config_var,acme,COOL_FEATURE_A COOL_FEATURE_B) define add_soong_config_var $(eval SOONG_CONFIG_$(strip $1) += $(strip $2)) \ $(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $(strip $($v)))) endef # The add_soong_config_var_value function defines a make variable and also adds # the variable to SOONG_CONFIG_*. # $1 is the namespace. $2 is the variable name. $3 is the variable value. # Ex: $(call add_soong_config_var_value,acme,COOL_FEATURE,true) define add_soong_config_var_value $(eval $(strip $2) := $(strip $3)) \ $(call add_soong_config_var,$1,$2) endef # Soong config namespace variables manipulation. # # internal utility to define a namespace and a variable in it. define soong_config_define_internal $(if $(filter $1,$(SOONG_CONFIG_NAMESPACES)),,$(eval SOONG_CONFIG_NAMESPACES:=$(SOONG_CONFIG_NAMESPACES) $(strip $1))) \ $(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $(strip $2))) endef # soong_config_set defines the variable in the given Soong config namespace # and sets its value. If the namespace does not exist, it will be defined. # $1 is the namespace. $2 is the variable name. $3 is the variable value. # Ex: $(call soong_config_set,acme,COOL_FEATURE,true) define soong_config_set $(call soong_config_define_internal,$1,$2) \ $(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3)) endef # soong_config_set_bool is the same as soong_config_set, but it will # also type the variable as a bool, so that when using select() expressions # in blueprint files they can use boolean values instead of strings. # It will only accept "true" for its value, any other value will be # treated as false. # $1 is the namespace. $2 is the variable name. $3 is the variable value. # Ex: $(call soong_config_set_bool,acme,COOL_FEATURE,true) define soong_config_set_bool $(call soong_config_define_internal,$1,$2) \ $(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(filter true,$3)) $(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=bool) endef # soong_config_set_int is the same as soong_config_set, but it will # also type the variable as an integer, so that when using select() expressions # in blueprint files they can use integer values instead of strings. # It will error out if a non-integer is supplied # $1 is the namespace. $2 is the variable name. $3 is the variable value. # Ex: $(call soong_config_set_bool,acme,COOL_FEATURE,34) define soong_config_set_int $(call soong_config_define_internal,$1,$2) \ $(if $(call math_is_int,$3),,$(error soong_config_set_int called with non-integer value $(3))) $(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3)) $(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=int) endef # soong_config_set_string_list is the same as soong_config_set, but it will # also type the variable as a list of strings, so that when using select() expressions # in blueprint files they can use list values instead of strings. # The values of the list must be space-separated. # $1 is the namespace. $2 is the variable name. $3 is the variable value. # Ex: $(call soong_config_set_string_list,acme,COOL_LIBS,a b) define soong_config_set_string_list $(call soong_config_define_internal,$1,$2) \ $(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3)) $(eval SOONG_CONFIG_TYPE_$(strip $1)_$(strip $2):=string_list) endef # soong_config_append appends to the value of the variable in the given Soong # config namespace. If the variable does not exist, it will be defined. If the # namespace does not exist, it will be defined. # $1 is the namespace, $2 is the variable name, $3 is the value define soong_config_append $(call soong_config_define_internal,$1,$2) \ $(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(SOONG_CONFIG_$(strip $1)_$(strip $2)) $(strip $3)) endef # soong_config_append gets to the value of the variable in the given Soong # config namespace. If the namespace or variables does not exist, an # empty string will be returned. # $1 is the namespace, $2 is the variable name define soong_config_get $(SOONG_CONFIG_$(strip $1)_$(strip $2)) endef # Set the extensions used for various packages COMMON_PACKAGE_SUFFIX := .zip COMMON_JAVA_PACKAGE_SUFFIX := .jar COMMON_ANDROID_PACKAGE_SUFFIX := .apk ifdef TMPDIR JAVA_TMPDIR_ARG := -Djava.io.tmpdir=$(TMPDIR) else JAVA_TMPDIR_ARG := endif # These build broken variables are intended to be set in a buildspec file, # while other build broken flags are expected to be set in a board config. # These are build broken variables that are expected to apply across board # configs, generally for cross-cutting features. # Build broken variables that should be treated as booleans _build_broken_bool_vars := # Build broken variables that should be treated as lists _build_broken_list_vars := \ BUILD_BROKEN_PLUGIN_VALIDATION \ _build_broken_var_names := $(_build_broken_bool_vars) _build_broken_var_names += $(_build_broken_list_vars) $(foreach v,$(_build_broken_var_names),$(eval $(v) :=)) # ############################################################### # Include sub-configuration files # ############################################################### # --------------------------------------------------------------- # Try to include buildspec.mk, which will try to set stuff up. # If this file doesn't exist, the environment variables will # be used, and if that doesn't work, then the default is an # arm build ifndef ANDROID_BUILDSPEC ANDROID_BUILDSPEC := $(TOPDIR)buildspec.mk endif -include $(ANDROID_BUILDSPEC) # --------------------------------------------------------------- # Define most of the global variables. These are the ones that # are specific to the user's build configuration. include $(BUILD_SYSTEM)/envsetup.mk $(foreach var,$(_build_broken_bool_vars), \ $(if $(filter-out true false,$($(var))), \ $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))"))) .KATI_READONLY := $(_build_broken_var_names) # Returns true if it is a low memory device, otherwise it returns false. define is-low-mem-device $(if $(findstring ro.config.low_ram=true,$(PRODUCT_PROPERTY_OVERRIDES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_COMPATIBLE_PROPERTY)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_SYSTEM_DEFAULT_PROPERTIES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_SYSTEM_EXT_PROPERTIES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_PRODUCT_PROPERTIES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_VENDOR_PROPERTIES)),true,\ $(if $(findstring ro.config.low_ram=true,$(PRODUCT_ODM_PROPERTIES)),true,false))))))))) endef # Set TARGET_MAX_PAGE_SIZE_SUPPORTED. # TARGET_MAX_PAGE_SIZE_SUPPORTED indicates the alignment of the ELF segments. ifdef PRODUCT_MAX_PAGE_SIZE_SUPPORTED TARGET_MAX_PAGE_SIZE_SUPPORTED := $(PRODUCT_MAX_PAGE_SIZE_SUPPORTED) else ifeq ($(strip $(call is-low-mem-device)),true) # Low memory device will have 4096 binary alignment. TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096 else ifeq ($(call math_lt,$(VSR_VENDOR_API_LEVEL),34),true) TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096 else ifeq (,$(filter arm64 x86_64,$(TARGET_ARCH))) # TARGET_MAX_PAGE_SIZE_SUPPORTED > 4096 is only supported in arm64 and # x86_64 targets. TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096 else # The default binary alignment for userspace is 16384. TARGET_MAX_PAGE_SIZE_SUPPORTED := 16384 endif .KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED # Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro. ifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO) else ifeq ($(call math_lt,$(VSR_VENDOR_API_LEVEL),35),true) TARGET_NO_BIONIC_PAGE_SIZE_MACRO := false else TARGET_NO_BIONIC_PAGE_SIZE_MACRO := true endif .KATI_READONLY := TARGET_NO_BIONIC_PAGE_SIZE_MACRO # Pruned directory options used when using findleaves.py # See envsetup.mk for a description of SCAN_EXCLUDE_DIRS FIND_LEAVES_EXCLUDES := $(addprefix --prune=, $(SCAN_EXCLUDE_DIRS) .repo .git) # The build system exposes several variables for where to find the kernel # headers: # TARGET_DEVICE_KERNEL_HEADERS is automatically created for the current # device being built. It is set as $(TARGET_DEVICE_DIR)/kernel-headers, # e.g. device/samsung/tuna/kernel-headers. This directory is not # explicitly set by anyone, the build system always adds this subdir. # # TARGET_BOARD_KERNEL_HEADERS is specified by the BoardConfig.mk file # to allow other directories to be included. This is useful if there's # some common place where a few headers are being kept for a group # of devices. For example, device//common/kernel-headers could # contain some headers for several of 's devices. # # TARGET_PRODUCT_KERNEL_HEADERS is generated by the product inheritance # graph. This allows architecture products to provide headers for the # devices using that architecture. For example, # hardware/ti/omap4xxx/omap4.mk will specify # PRODUCT_VENDOR_KERNEL_HEADERS variable that specify where the omap4 # specific headers are, e.g. hardware/ti/omap4xxx/kernel-headers. # The build system then combines all the values specified by all the # PRODUCT_VENDOR_KERNEL_HEADERS directives in the product inheritance # tree and then exports a TARGET_PRODUCT_KERNEL_HEADERS variable. # # The layout of subdirs in any of the kernel-headers dir should mirror the # layout of the kernel include/ directory. For example, # device/samsung/tuna/kernel-headers/linux/, # hardware/ti/omap4xxx/kernel-headers/media/, # etc. # # NOTE: These directories MUST contain post-processed headers using the # bionic/libc/kernel/tools/clean_header.py tool. Additionally, the original # kernel headers must also be checked in, but in a different subdirectory. By # convention, the originals should be checked into original-kernel-headers # directory of the same parent dir. For example, # device/samsung/tuna/kernel-headers <----- post-processed # device/samsung/tuna/original-kernel-headers <----- originals # TARGET_DEVICE_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_DEVICE_DIR)/kernel-headers)) define validate-kernel-headers $(if $(firstword $(foreach hdr_dir,$(1),\ $(filter-out kernel-headers,$(notdir $(hdr_dir))))),\ $(error Kernel header dirs must be end in kernel-headers: $(1))) endef # also allow the board config to provide additional directories since # there could be device/oem/base_hw and device/oem/derived_hw # that both are valid devices but derived_hw needs to use kernel headers # from base_hw. TARGET_BOARD_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_BOARD_KERNEL_HEADERS))) TARGET_BOARD_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_BOARD_KERNEL_HEADERS)) $(call validate-kernel-headers,$(TARGET_BOARD_KERNEL_HEADERS)) # then add product-inherited includes, to allow for # hardware/sivendor/chip/chip.mk to include their own headers TARGET_PRODUCT_KERNEL_HEADERS := $(strip $(wildcard $(PRODUCT_VENDOR_KERNEL_HEADERS))) TARGET_PRODUCT_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_PRODUCT_KERNEL_HEADERS)) $(call validate-kernel-headers,$(TARGET_PRODUCT_KERNEL_HEADERS)) .KATI_READONLY := TARGET_DEVICE_KERNEL_HEADERS TARGET_BOARD_KERNEL_HEADERS TARGET_PRODUCT_KERNEL_HEADERS # Commands to generate .toc file common to ELF .so files. define _gen_toc_command_for_elf $(hide) ($($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) -d $(1) | grep SONAME || echo "No SONAME for $1") > $(2) $(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)READELF) --dyn-syms $(1) | awk '{$$2=""; $$3=""; print}' >> $(2) endef # Commands to generate .toc file from Darwin dynamic library. define _gen_toc_command_for_macho $(hide) $(HOST_OTOOL) -l $(1) | grep LC_ID_DYLIB -A 5 > $(2) $(hide) $(HOST_NM) -gP $(1) | cut -f1-2 -d" " | (grep -v U$$ >> $(2) || true) endef # Pick a Java compiler. include $(BUILD_SYSTEM)/combo/javac.mk ifeq ($(CALLED_FROM_SETUP),true) include $(BUILD_SYSTEM)/ccache.mk include $(BUILD_SYSTEM)/rbe.mk endif # GCC version selection TARGET_GCC_VERSION := 4.9 ifdef TARGET_2ND_ARCH 2ND_TARGET_GCC_VERSION := 4.9 endif # Normalize WITH_STATIC_ANALYZER ifeq ($(strip $(WITH_STATIC_ANALYZER)),0) WITH_STATIC_ANALYZER := endif # Unset WITH_TIDY_ONLY if global WITH_TIDY_ONLY is not true nor 1. ifeq (,$(filter 1 true,$(WITH_TIDY_ONLY))) WITH_TIDY_ONLY := endif # --------------------------------------------------------------- # Check that the configuration is current. We check that # BUILD_ENV_SEQUENCE_NUMBER is current against this value. # Don't fail if we're called from envsetup, so they have a # chance to update their environment. ifeq (,$(strip $(CALLED_FROM_SETUP))) ifneq (,$(strip $(BUILD_ENV_SEQUENCE_NUMBER))) ifneq ($(BUILD_ENV_SEQUENCE_NUMBER),$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)) $(warning BUILD_ENV_SEQUENCE_NUMBER is set incorrectly.) $(info *** If you use envsetup/lunch/choosecombo:) $(info *** - Re-execute envsetup (". envsetup.sh")) $(info *** - Re-run lunch or choosecombo) $(info *** If you use buildspec.mk:) $(info *** - Look at buildspec.mk.default to see what has changed) $(info *** - Update BUILD_ENV_SEQUENCE_NUMBER to "$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)") $(error bailing..) endif endif endif # --------------------------------------------------------------- # Whether we can expect a full build graph ALLOW_MISSING_DEPENDENCIES := $(filter true,$(ALLOW_MISSING_DEPENDENCIES)) ifneq ($(TARGET_BUILD_APPS),) ALLOW_MISSING_DEPENDENCIES := true endif ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true) ALLOW_MISSING_DEPENDENCIES := true endif ifneq ($(filter true,$(SOONG_ALLOW_MISSING_DEPENDENCIES)),) ALLOW_MISSING_DEPENDENCIES := true endif # Mac builds default to ALLOW_MISSING_DEPENDENCIES, at least until the host # tools aren't enabled by default for Mac. ifeq ($(HOST_OS),darwin) ALLOW_MISSING_DEPENDENCIES := true endif .KATI_READONLY := ALLOW_MISSING_DEPENDENCIES TARGET_BUILD_USE_PREBUILT_SDKS := DISABLE_PREOPT := DISABLE_PREOPT_BOOT_IMAGES := ifneq (,$(TARGET_BUILD_APPS)$(TARGET_BUILD_UNBUNDLED_IMAGE)) DISABLE_PREOPT := true DISABLE_PREOPT_BOOT_IMAGES := true endif ifeq (true,$(TARGET_BUILD_UNBUNDLED)) ifneq (true,$(UNBUNDLED_BUILD_SDKS_FROM_SOURCE)) TARGET_BUILD_USE_PREBUILT_SDKS := true endif endif .KATI_READONLY := \ TARGET_BUILD_USE_PREBUILT_SDKS \ DISABLE_PREOPT \ DISABLE_PREOPT_BOOT_IMAGES \ prebuilt_sdk_tools := prebuilts/sdk/tools prebuilt_sdk_tools_bin := $(prebuilt_sdk_tools)/$(HOST_OS)/bin prebuilt_build_tools := prebuilts/build-tools prebuilt_build_tools_wrappers := prebuilts/build-tools/common/bin prebuilt_build_tools_jars := prebuilts/build-tools/common/framework prebuilt_build_tools_bin_noasan := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/bin ifeq ($(filter address,$(SANITIZE_HOST)),) prebuilt_build_tools_bin := $(prebuilt_build_tools_bin_noasan) else prebuilt_build_tools_bin := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/asan/bin endif # Work around for b/68406220 # This should match the soong version. USE_D8 := true .KATI_READONLY := USE_D8 # # Tools that are prebuilts for TARGET_BUILD_USE_PREBUILT_SDKS # ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) AAPT := $(HOST_OUT_EXECUTABLES)/aapt else # TARGET_BUILD_USE_PREBUILT_SDKS AAPT := $(prebuilt_sdk_tools_bin)/aapt endif # TARGET_BUILD_USE_PREBUILT_SDKS ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) # Use RenderScript prebuilts for unbundled builds LLVM_RS_CC := $(HOST_OUT_EXECUTABLES)/llvm-rs-cc BCC_COMPAT := $(HOST_OUT_EXECUTABLES)/bcc_compat else LLVM_RS_CC := $(prebuilt_sdk_tools_bin)/llvm-rs-cc BCC_COMPAT := $(prebuilt_sdk_tools_bin)/bcc_compat endif prebuilt_sdk_tools := prebuilt_sdk_tools_bin := ACP := $(prebuilt_build_tools_bin)/acp CKATI := $(prebuilt_build_tools_bin)/ckati DEPMOD := $(HOST_OUT_EXECUTABLES)/depmod FILESLIST := $(HOST_OUT_EXECUTABLES)/fileslist FILESLIST_UTIL :=$= build/make/tools/fileslist_util.py HOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier XMLLINT := $(HOST_OUT_EXECUTABLES)/xmllint ACONFIG := $(HOST_OUT_EXECUTABLES)/aconfig # SOONG_ZIP is exported by Soong, but needs to be defined early for # $OUT/dexpreopt.global. It will be verified against the Soong version. SOONG_ZIP := $(HOST_OUT_EXECUTABLES)/soong_zip # --------------------------------------------------------------- # Generic tools. # These dependencies are now handled via dependencies on prebuilt_build_tool BISON_DATA :=$= YASM := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/yasm/yasm DOXYGEN:= doxygen ifeq ($(HOST_OS),linux) BREAKPAD_DUMP_SYMS := $(HOST_OUT_EXECUTABLES)/dump_syms else # For non-supported hosts, do not generate breakpad symbols. BREAKPAD_GENERATE_SYMBOLS := false endif GZIP := prebuilts/build-tools/path/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/gzip PROTOC := $(HOST_OUT_EXECUTABLES)/aprotoc$(HOST_EXECUTABLE_SUFFIX) NANOPB_SRCS := $(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX) MINIGZIP := $(GZIP) LZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX) ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG))) MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX) else MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG) endif ifeq (,$(strip $(BOARD_CUSTOM_AVBTOOL))) AVBTOOL := $(HOST_OUT_EXECUTABLES)/avbtool$(HOST_EXECUTABLE_SUFFIX) else AVBTOOL := $(BOARD_CUSTOM_AVBTOOL) endif APICHECK := $(HOST_OUT_JAVA_LIBRARIES)/metalava$(COMMON_JAVA_PACKAGE_SUFFIX) MKEXTUSERIMG := $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs MKE2FS_CONF := system/extras/ext4_utils/mke2fs.conf MKEROFS := $(HOST_OUT_EXECUTABLES)/mkfs.erofs MKSQUASHFSUSERIMG := $(HOST_OUT_EXECUTABLES)/mksquashfsimage MKF2FSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg SIMG2IMG := $(HOST_OUT_EXECUTABLES)/simg2img$(HOST_EXECUTABLE_SUFFIX) E2FSCK := $(HOST_OUT_EXECUTABLES)/e2fsck$(HOST_EXECUTABLE_SUFFIX) TUNE2FS := $(HOST_OUT_EXECUTABLES)/tune2fs$(HOST_EXECUTABLE_SUFFIX) JARJAR := $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar DATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar FAT16COPY := build/make/tools/fat16copy.py CHECK_ELF_FILE := $(HOST_OUT_EXECUTABLES)/check_elf_file$(HOST_EXECUTABLE_SUFFIX) LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX) ADD_IMG_TO_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/add_img_to_target_files$(HOST_EXECUTABLE_SUFFIX) BUILD_IMAGE := $(HOST_OUT_EXECUTABLES)/build_image$(HOST_EXECUTABLE_SUFFIX) ifeq (,$(strip $(BOARD_CUSTOM_BUILD_SUPER_IMAGE))) BUILD_SUPER_IMAGE := $(HOST_OUT_EXECUTABLES)/build_super_image$(HOST_EXECUTABLE_SUFFIX) else BUILD_SUPER_IMAGE := $(BOARD_CUSTOM_BUILD_SUPER_IMAGE) endif IMG_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/img_from_target_files$(HOST_EXECUTABLE_SUFFIX) UNPACK_BOOTIMG := $(HOST_OUT_EXECUTABLES)/unpack_bootimg MAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX) OTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX) OTA_FROM_RAW_IMG := $(HOST_OUT_EXECUTABLES)/ota_from_raw_img$(HOST_EXECUTABLE_SUFFIX) SPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX) CHECK_PARTITION_SIZES := $(HOST_OUT_EXECUTABLES)/check_partition_sizes$(HOST_EXECUTABLE_SUFFIX) SYMBOLS_MAP := $(HOST_OUT_EXECUTABLES)/symbols_map PROGUARD_HOME := external/proguard PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh PROGUARD_DEPS := $(PROGUARD) $(PROGUARD_HOME)/lib/proguard.jar JAVATAGS := $(HOST_OUT_EXECUTABLES)/java-event-log-tags MERGETAGS := $(HOST_OUT_EXECUTABLES)/merge-event-log-tags APPEND2SIMG := $(HOST_OUT_EXECUTABLES)/append2simg VERITY_SIGNER := $(HOST_OUT_EXECUTABLES)/verity_signer BUILD_VERITY_METADATA := $(HOST_OUT_EXECUTABLES)/build_verity_metadata BUILD_VERITY_TREE := $(HOST_OUT_EXECUTABLES)/build_verity_tree DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump$(BUILD_EXECUTABLE_SUFFIX) PROFMAN := $(HOST_OUT_EXECUTABLES)/profman GEN_SBOM := $(HOST_OUT_EXECUTABLES)/generate-sbom FINDBUGS_DIR := external/owasp/sanitizer/tools/findbugs/bin FINDBUGS := $(FINDBUGS_DIR)/findbugs JETIFIER := prebuilts/sdk/tools/jetifier/jetifier-standalone/bin/jetifier-standalone EXTRACT_KERNEL := build/make/tools/extract_kernel.py # Path to tools.jar HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) # Boolean variable determining if the allow list for compatible properties is enabled PRODUCT_COMPATIBLE_PROPERTY := true ifeq ($(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE),false) $(error PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE is obsolete) endif .KATI_READONLY := \ PRODUCT_COMPATIBLE_PROPERTY # TODO: remove all code referencing these, and remove override variables PRODUCT_FULL_TREBLE := true PRODUCT_TREBLE_LINKER_NAMESPACES := true PRODUCT_ENFORCE_VINTF_MANIFEST := true # TODO(b/114488870): disallow PRODUCT_FULL_TREBLE_OVERRIDE from being used. .KATI_READONLY := \ PRODUCT_FULL_TREBLE \ PRODUCT_TREBLE_LINKER_NAMESPACES \ PRODUCT_ENFORCE_VINTF_MANIFEST \ # TODO(b/114488870): remove all sets of these everwhere, and disallow them to be used $(KATI_obsolete_var PRODUCT_TREBLE_LINKER_NAMESPACES_OVERRIDE,Deprecated.) $(KATI_obsolete_var PRODUCT_ENFORCE_VINTF_MANIFEST_OVERRIDE,Deprecated.) $(KATI_obsolete_var PRODUCT_FULL_TREBLE_OVERRIDE,Deprecated.) # BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of # partitions is supported. But the early-mount must be supported for full # treble products, and so BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED should be set # by default for full treble products. ifeq ($(PRODUCT_FULL_TREBLE),true) BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED ?= true endif ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),) ifneq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),) $(error Must not set NEED_AIDL_NDK_PLATFORM_BACKEND, but it is set to: $(NEED_AIDL_NDK_PLATFORM_BACKEND). Support will be removed.) endif endif ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE) else ifeq (true,$(TARGET_BUILD_UNBUNDLED)) # unbundled builds may not have updated build sources TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false else ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),) TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := true else TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false endif .KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE # Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching # devices if unset. ifndef BOARD_SYSTEMSDK_VERSIONS ifeq (REL,$(PLATFORM_VERSION_CODENAME)) BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_SDK_VERSION) else BOARD_SYSTEMSDK_VERSIONS := $(PLATFORM_VERSION_CODENAME) endif endif ifndef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES := current else ifdef PRODUCT_SHIPPING_API_LEVEL ifneq ($(call math_lt,$(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES),$(PRODUCT_SHIPPING_API_LEVEL)),) $(error BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES ($(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) must be greater than or equal to PRODUCT_SHIPPING_API_LEVEL ($(PRODUCT_SHIPPING_API_LEVEL))) endif endif endif .KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES ifdef PRODUCT_SHIPPING_API_LEVEL ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),) ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),) $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set) endif endif endif # The default key if not set as LOCAL_CERTIFICATE ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE) else DEFAULT_SYSTEM_DEV_CERTIFICATE := build/make/target/product/security/testkey endif .KATI_READONLY := DEFAULT_SYSTEM_DEV_CERTIFICATE # Certificate for the NetworkStack sepolicy context ifdef PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES) else MAINLINE_SEPOLICY_DEV_CERTIFICATES := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE)) endif .KATI_READONLY := MAINLINE_SEPOLICY_DEV_CERTIFICATES BUILD_NUMBER_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_number.txt) BUILD_HOSTNAME_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_hostname.txt) BUILD_DATETIME_FROM_FILE := $$(cat $(BUILD_DATETIME_FILE)) # SEPolicy versions # PLATFORM_SEPOLICY_VERSION is a number of the form "YYYYMM" with "YYYYMM" # mapping to vFRC version. PLATFORM_SEPOLICY_VERSION := $(BOARD_API_LEVEL) BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION) .KATI_READONLY := PLATFORM_SEPOLICY_VERSION BOARD_SEPOLICY_VERS # A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports. PLATFORM_SEPOLICY_COMPAT_VERSIONS := \ 29.0 \ 30.0 \ 31.0 \ 32.0 \ 33.0 \ 34.0 \ PLATFORM_SEPOLICY_COMPAT_VERSIONS += $(foreach ver,\ 202404 \ 202504 \ ,$(if $(filter true,$(call math_gt,$(PLATFORM_SEPOLICY_VERSION),$(ver))),$(ver))) .KATI_READONLY := \ PLATFORM_SEPOLICY_COMPAT_VERSIONS \ PLATFORM_SEPOLICY_VERSION \ BOARD_GENFS_LABELS_VERSION ?= $(BOARD_API_LEVEL) ifeq ($(call math_gt,$(BOARD_API_LEVEL),$(BOARD_GENFS_LABELS_VERSION)),true) $(error BOARD_GENFS_LABELS_VERSION ($(BOARD_GENFS_LABELS_VERSION)) must be greater than or equal to BOARD_API_LEVEL ($(BOARD_API_LEVEL))) endif ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) $(error PRODUCT_USE_DYNAMIC_PARTITIONS must be true when PRODUCT_RETROFIT_DYNAMIC_PARTITIONS \ is set) endif ifdef PRODUCT_SHIPPING_API_LEVEL ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29)) $(error Devices with shipping API level $(PRODUCT_SHIPPING_API_LEVEL) must not set \ PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) endif endif endif ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) ifneq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true) $(error PRODUCT_USE_DYNAMIC_PARTITION_SIZE must be true for devices with dynamic partitions) endif endif ifeq ($(PRODUCT_BUILD_SUPER_PARTITION),true) ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) $(error Can only build super partition for devices with dynamic partitions) endif endif ifeq ($(PRODUCT_USE_DYNAMIC_PARTITION_SIZE),true) ifneq ($(BOARD_SYSTEMIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_SYSTEMIMAGE_PARTITION_SIZE and \ BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_VENDORIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_VENDORIMAGE_PARTITION_SIZE and \ BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_ODMIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_ODMIMAGE_PARTITION_SIZE and \ BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE and \ BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_ODM_DLKMIMAGE_PARTITION_SIZE and \ BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_SYSTEM_DLKMIMAGE_PARTITION_SIZE and \ BOARD_SYSTEM_DLKMIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_PRODUCTIMAGE_PARTITION_SIZE and \ BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE together) endif endif ifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE),) ifneq ($(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE),) $(error Should not define BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE and \ BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE together) endif endif endif # PRODUCT_USE_DYNAMIC_PARTITION_SIZE ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) # BOARD_SUPER_PARTITION_GROUPS defines a list of "updatable groups". Each updatable group is a # group of partitions that share the same pool of free spaces. # For each group in BOARD_SUPER_PARTITION_GROUPS, a BOARD_{GROUP}_SIZE and # BOARD_{GROUP}_PARTITION_PARTITION_LIST may be defined. # - BOARD_{GROUP}_SIZE: The maximum sum of sizes of all partitions in the group. # Must not be empty. # - BOARD_{GROUP}_PARTITION_PARTITION_LIST: the list of partitions that belongs to this group. # If empty, no partitions belong to this group, and the sum of sizes is effectively 0. $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \ $(eval BOARD_$(group)_SIZE := $(strip $(BOARD_$(group)_SIZE))) \ $(if $(BOARD_$(group)_SIZE),,$(error BOARD_$(group)_SIZE must not be empty)) \ $(eval .KATI_READONLY := BOARD_$(group)_SIZE) \ $(eval BOARD_$(group)_PARTITION_LIST ?=) \ $(eval .KATI_READONLY := BOARD_$(group)_PARTITION_LIST) \ ) # Define BOARD_SUPER_PARTITION_PARTITION_LIST, the sum of all BOARD_*_PARTITION_LIST ifdef BOARD_SUPER_PARTITION_PARTITION_LIST $(error BOARD_SUPER_PARTITION_PARTITION_LIST should not be defined, but computed from \ BOARD_SUPER_PARTITION_GROUPS and BOARD_*_PARTITION_LIST) endif BOARD_SUPER_PARTITION_PARTITION_LIST := \ $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)),$(BOARD_$(group)_PARTITION_LIST)) .KATI_READONLY := BOARD_SUPER_PARTITION_PARTITION_LIST ifneq ($(BOARD_SUPER_PARTITION_SIZE),) ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true) # The metadata device must be specified manually for retrofitting. ifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),) $(error Must specify BOARD_SUPER_PARTITION_METADATA_DEVICE if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.) endif # The super partition block device list must be specified manually for retrofitting. ifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),) $(error Must specify BOARD_SUPER_PARTITION_BLOCK_DEVICES if PRODUCT_RETROFIT_DYNAMIC_PARTITIONS=true.) endif # The metadata device must be included in the super partition block device list. ifeq (,$(filter $(BOARD_SUPER_PARTITION_METADATA_DEVICE),$(BOARD_SUPER_PARTITION_BLOCK_DEVICES))) $(error BOARD_SUPER_PARTITION_METADATA_DEVICE is not listed in BOARD_SUPER_PARTITION_BLOCK_DEVICES.) endif # The metadata device must be supplied to init via the kernel command-line. INTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE) BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := true # If "vendor" is listed as one of the dynamic partitions but without its image available (e.g. an # AOSP target built without vendor image), don't build the retrofit full OTA package. Because we # won't be able to build meaningful super_* images for retrofitting purpose. ifneq (,$(filter vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST))) ifndef BUILDING_VENDOR_IMAGE ifndef BOARD_PREBUILT_VENDORIMAGE BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := endif # BOARD_PREBUILT_VENDORIMAGE endif # BUILDING_VENDOR_IMAGE endif # BOARD_SUPER_PARTITION_PARTITION_LIST else # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS # For normal devices, we populate BOARD_SUPER_PARTITION_BLOCK_DEVICES so the # build can handle both cases consistently. ifeq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),) BOARD_SUPER_PARTITION_METADATA_DEVICE := super endif ifeq ($(BOARD_SUPER_PARTITION_BLOCK_DEVICES),) BOARD_SUPER_PARTITION_BLOCK_DEVICES := $(BOARD_SUPER_PARTITION_METADATA_DEVICE) endif # If only one super block device, default to super partition size. ifeq ($(word 2,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),) BOARD_SUPER_PARTITION_$(call to-upper,$(strip $(BOARD_SUPER_PARTITION_BLOCK_DEVICES)))_DEVICE_SIZE ?= \ $(BOARD_SUPER_PARTITION_SIZE) endif ifneq ($(BOARD_SUPER_PARTITION_METADATA_DEVICE),super) INTERNAL_KERNEL_CMDLINE += androidboot.super_partition=$(BOARD_SUPER_PARTITION_METADATA_DEVICE) endif BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE := endif # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS endif # BOARD_SUPER_PARTITION_SIZE BOARD_SUPER_PARTITION_BLOCK_DEVICES ?= .KATI_READONLY := BOARD_SUPER_PARTITION_BLOCK_DEVICES BOARD_SUPER_PARTITION_METADATA_DEVICE ?= .KATI_READONLY := BOARD_SUPER_PARTITION_METADATA_DEVICE BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE ?= .KATI_READONLY := BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE $(foreach device,$(call to-upper,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)), \ $(eval BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE := $(strip $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE))) \ $(if $(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE),, \ $(error BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE must not be empty)) \ $(eval .KATI_READONLY := BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE)) endif # PRODUCT_USE_DYNAMIC_PARTITIONS # By default, we build the hidden API csv files from source. You can use # prebuilt hiddenapi files by setting BOARD_PREBUILT_HIDDENAPI_DIR to the name # of a directory containing both prebuilt hiddenapi-flags.csv and # hiddenapi-index.csv. BOARD_PREBUILT_HIDDENAPI_DIR ?= .KATI_READONLY := BOARD_PREBUILT_HIDDENAPI_DIR # ############################################################### # Set up final options. # ############################################################### # We run gcc/clang with PWD=/proc/self/cwd to remove the $TOP # from the debug output. That way two builds in two different # directories will create the same output. # /proc doesn't exist on Darwin. ifeq ($(HOST_OS),linux) RELATIVE_PWD := PWD=/proc/self/cwd else RELATIVE_PWD := endif # Flags for DEX2OAT first_non_empty_of_three = $(if $(1),$(1),$(if $(2),$(2),$(3))) DEX2OAT_TARGET_ARCH := $(TARGET_ARCH) DEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT),$(TARGET_ARCH_VARIANT),default) DEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_ARCH_VARIANT),default) DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default ifdef TARGET_2ND_ARCH $(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH := $(TARGET_2ND_ARCH) $(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT),$(TARGET_2ND_ARCH_VARIANT),default) $(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME := $(call first_non_empty_of_three,$(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_ARCH_VARIANT),default) $(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES := default endif # ############################################################### # Collect a list of the SDK versions that we could compile against # For use with the LOCAL_SDK_VERSION variable for include $(BUILD_PACKAGE) # ############################################################### HISTORICAL_SDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/sdk HISTORICAL_NDK_VERSIONS_ROOT := $(TOPDIR)prebuilts/ndk # The path where app can reference the support library resources. ifdef TARGET_BUILD_USE_PREBUILT_SDKS SUPPORT_LIBRARY_ROOT := $(HISTORICAL_SDK_VERSIONS_ROOT)/current/support else SUPPORT_LIBRARY_ROOT := frameworks/support endif get-sdk-version = $(if $(findstring _,$(1)),$(subst core_,,$(subst system_,,$(subst test_,,$(1)))),$(1)) get-sdk-api = $(if $(findstring _,$(1)),$(patsubst %_$(call get-sdk-version,$(1)),%,$(1)),public) get-prebuilt-sdk-dir = $(HISTORICAL_SDK_VERSIONS_ROOT)/$(call get-sdk-version,$(1))/$(call get-sdk-api,$(1)) # Resolve LOCAL_SDK_VERSION to prebuilt module name, e.g.: # 23 -> sdk_public_23_android # system_current -> sdk_system_current_android # $(1): An sdk version (LOCAL_SDK_VERSION) # $(2): optional library name (default: android) define resolve-prebuilt-sdk-module $(if $(findstring _,$(1)),\ sdk_$(1)_$(or $(2),android),\ sdk_public_$(1)_$(or $(2),android)) endef # Resolve LOCAL_SDK_VERSION to prebuilt android.jar # $(1): LOCAL_SDK_VERSION resolve-prebuilt-sdk-jar-path = $(call get-prebuilt-sdk-dir,$(1))/android.jar # Resolve LOCAL_SDK_VERSION to prebuilt framework.aidl # $(1): An sdk version (LOCAL_SDK_VERSION) resolve-prebuilt-sdk-aidl-path = $(call get-prebuilt-sdk-dir,$(call get-sdk-version,$(1)))/framework.aidl # Historical SDK version N is stored in $(HISTORICAL_SDK_VERSIONS_ROOT)/N. # The 'current' version is whatever this source tree is. # # sgrax is the opposite of xargs. It takes the list of args and puts them # on each line for sort to process. # sort -g is a numeric sort, so 1 2 3 10 instead of 1 10 2 3. # Numerically sort a list of numbers # $(1): the list of numbers to be sorted define numerically_sort $(shell function sgrax() { \ while [ -n "$$1" ] ; do echo $$1 ; shift ; done \ } ; \ ( sgrax $(1) | sort -g ) ) endef # This produces a list like "current/core current/public current/system 4/public" TARGET_AVAILABLE_SDK_VERSIONS := $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/*/android.jar) TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/android.jar,%,$(TARGET_AVAILABLE_SDK_VERSIONS)) # Strips and reorganizes the "public", "core", "system" and "test" subdirs. TARGET_AVAILABLE_SDK_VERSIONS := $(subst /public,,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/core,core_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/system,system_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_AVAILABLE_SDK_VERSIONS := $(patsubst %/test,test_%,$(TARGET_AVAILABLE_SDK_VERSIONS)) # module-lib and system-server are not supported in Make. TARGET_AVAILABLE_SDK_VERSIONS := $(filter-out %/module-lib %/system-server,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_AVAIALBLE_SDK_VERSIONS := $(call numerically_sort,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT := $(call numbers_less_than,30,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT := $(call numbers_less_than,32,$(TARGET_AVAILABLE_SDK_VERSIONS)) TARGET_SDK_VERSIONS_WITHOUT_JAVA_17_SUPPORT := $(call numbers_less_than,34,$(TARGET_AVAILABLE_SDK_VERSIONS)) JAVA_LANGUAGE_VERSIONS_WITHOUT_SYSTEM_MODULES := 1.7 1.8 # This is the standard way to name a directory containing prebuilt target # objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so TARGET_PREBUILT_TAG := android-$(TARGET_ARCH) ifdef TARGET_2ND_ARCH TARGET_2ND_PREBUILT_TAG := android-$(TARGET_2ND_ARCH) endif # Set up RS prebuilt variables for compatibility library RS_PREBUILT_CLCORE := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/librsrt_$(TARGET_ARCH).bc RS_PREBUILT_COMPILER_RT := prebuilts/sdk/renderscript/lib/$(TARGET_ARCH)/libcompiler_rt.a # API Level lists for Renderscript Compat lib. RSCOMPAT_32BIT_ONLY_API_LEVELS := 8 9 10 11 12 13 14 15 16 17 18 19 20 RSCOMPAT_NO_USAGEIO_API_LEVELS := 8 9 10 11 12 13 APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION) # Add BUILD_NUMBER to apps if PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER is defined. ifeq ($(PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER),true) APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE) endif # ANDROID_WARNING_ALLOWED_PROJECTS is generated by build/soong. define find_warning_allowed_projects $(filter $(ANDROID_WARNING_ALLOWED_PROJECTS),$(1)/) endef GOMA_POOL := RBE_POOL := GOMA_OR_RBE_POOL := # When goma or RBE are enabled, kati will be passed --default_pool=local_pool to put # most rules into the local pool. Explicitly set the pool to "none" for rules that # should be run outside the local pool, i.e. with -j500. ifneq (,$(filter-out false,$(USE_GOMA))) GOMA_POOL := none GOMA_OR_RBE_POOL := none else ifneq (,$(filter-out false,$(USE_RBE))) RBE_POOL := none GOMA_OR_RBE_POOL := none endif .KATI_READONLY := GOMA_POOL RBE_POOL GOMA_OR_RBE_POOL JAVAC_NINJA_POOL := R8_NINJA_POOL := D8_NINJA_POOL := ifneq ($(filter-out false,$(USE_RBE)),) ifdef RBE_JAVAC JAVAC_NINJA_POOL := $(RBE_POOL) endif ifdef RBE_R8 R8_NINJA_POOL := $(RBE_POOL) endif ifdef RBE_D8 D8_NINJA_POOL := $(RBE_POOL) endif endif .KATI_READONLY := JAVAC_NINJA_POOL R8_NINJA_POOL D8_NINJA_POOL # Soong modules that are known to have broken optional_uses_libs dependencies. BUILD_WARNING_BAD_OPTIONAL_USES_LIBS_ALLOWLIST := LegacyCamera Gallery2 # These goals don't need to collect and include Android.mks/CleanSpec.mks # in the source tree. dont_bother_goals := out product-graph ifeq ($(TARGET_SYSTEM_PROP),) TARGET_SYSTEM_PROP := $(wildcard $(TARGET_DEVICE_DIR)/system.prop) endif ifeq ($(TARGET_SYSTEM_EXT_PROP),) TARGET_SYSTEM_EXT_PROP := $(wildcard $(TARGET_DEVICE_DIR)/system_ext.prop) endif ifeq ($(TARGET_PRODUCT_PROP),) TARGET_PRODUCT_PROP := $(wildcard $(TARGET_DEVICE_DIR)/product.prop) endif ifeq ($(TARGET_ODM_PROP),) TARGET_ODM_PROP := $(wildcard $(TARGET_DEVICE_DIR)/odm.prop) endif .KATI_READONLY := \ TARGET_SYSTEM_PROP \ TARGET_SYSTEM_EXT_PROP \ TARGET_PRODUCT_PROP \ TARGET_ODM_PROP \ include $(BUILD_SYSTEM)/sysprop_config.mk # Make ANDROID Soong config variables visible to Android.mk files, for # consistency with those defined in BoardConfig.mk files. include $(BUILD_SYSTEM)/android_soong_config_vars.mk # EMMA_INSTRUMENT is set to true when coverage is enabled. Creates a suffix to # differeciate the coverage version of ninja files. This will save 5 minutes of # build time used to regenerate ninja. ifeq (true,$(EMMA_INSTRUMENT)) COVERAGE_SUFFIX := .coverage endif SOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).variables SOONG_EXTRA_VARIABLES := $(SOONG_OUT_DIR)/soong.$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).extra.variables ifeq ($(CALLED_FROM_SETUP),true) include $(BUILD_SYSTEM)/ninja_config.mk include $(BUILD_SYSTEM)/soong_config.mk endif SOONG_VARIABLES := SOONG_EXTRA_VARIABLES := include $(BUILD_SYSTEM)/dumpvar.mk ifdef BOARD_VNDK_VERSION BOARD_VNDK_VERSION= endif ifdef PLATFORM_VNDK_VERSION PLATFORM_VNDK_VERSION= endif ifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA)) ifeq (false,$(SYSTEM_OPTIMIZE_JAVA)) $(error SYSTEM_OPTIMIZE_JAVA must be enabled when FULL_SYSTEM_OPTIMIZE_JAVA is enabled) endif endif # ----------------------------------------------------------------- # Define fingerprint, thumbprint, and version tags for the current build # # BUILD_VERSION_TAGS is a comma-separated list of tags chosen by the device # implementer that further distinguishes the build. It's basically defined # by the device implementer. Here, we are adding a mandatory tag that # identifies the signing config of the build. BUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS) ifeq ($(TARGET_BUILD_TYPE),debug) BUILD_VERSION_TAGS += debug endif # The "test-keys" tag marks builds signed with the old test keys, # which are available in the SDK. "dev-keys" marks builds signed with # non-default dev keys (usually private keys from a vendor directory). # Both of these tags will be removed and replaced with "release-keys" # when the target-files is signed in a post-build step. ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/make/target/product/security/testkey) BUILD_KEYS := test-keys else BUILD_KEYS := dev-keys endif BUILD_VERSION_TAGS += $(BUILD_KEYS) BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS))) # BUILD_FINGERPRINT is used used to uniquely identify the combined build and # product; used by the OTA server. ifeq (,$(strip $(BUILD_FINGERPRINT))) BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS) endif BUILD_FINGERPRINT_FILE := $(PRODUCT_OUT)/build_fingerprint.txt ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_FINGERPRINT) >$(BUILD_FINGERPRINT_FILE).tmp && (if ! cmp -s $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); then mv $(BUILD_FINGERPRINT_FILE).tmp $(BUILD_FINGERPRINT_FILE); else rm $(BUILD_FINGERPRINT_FILE).tmp; fi) && grep " " $(BUILD_FINGERPRINT_FILE))) $(error BUILD_FINGERPRINT cannot contain spaces: "$(file <$(BUILD_FINGERPRINT_FILE))") endif BUILD_FINGERPRINT_FROM_FILE := $$(cat $(BUILD_FINGERPRINT_FILE)) # unset it for safety. BUILD_FINGERPRINT := # BUILD_THUMBPRINT is used to uniquely identify the system build; used by the # OTA server. This purposefully excludes any product-specific variables. ifeq (,$(strip $(BUILD_THUMBPRINT))) BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER_FROM_FILE):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS) endif BUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt ifeq ($(strip $(HAS_BUILD_NUMBER)),true) $(BUILD_THUMBPRINT_FILE): $(BUILD_NUMBER_FILE) endif ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE))) $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))") endif # unset it for safety. BUILD_THUMBPRINT_FILE := BUILD_THUMBPRINT := ================================================ FILE: core/config_sanitizers.mk ================================================ ############################################## ## Perform configuration steps for sanitizers. ############################################## my_sanitize := $(strip $(LOCAL_SANITIZE)) my_sanitize_diag := $(strip $(LOCAL_SANITIZE_DIAG)) my_global_sanitize := my_global_sanitize_diag := ifdef LOCAL_IS_HOST_MODULE ifneq ($($(my_prefix)OS),windows) my_global_sanitize := $(strip $(SANITIZE_HOST)) # SANITIZE_HOST=true is a deprecated way to say SANITIZE_HOST=address. my_global_sanitize := $(subst true,address,$(my_global_sanitize)) endif else my_global_sanitize := $(strip $(SANITIZE_TARGET)) my_global_sanitize_diag := $(strip $(SANITIZE_TARGET_DIAG)) endif # Disable global integer_overflow in excluded paths. ifneq ($(filter integer_overflow, $(my_global_sanitize)),) combined_exclude_paths := $(INTEGER_OVERFLOW_EXCLUDE_PATHS) \ $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) endif endif # Global integer sanitization doesn't support static modules. ifeq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),) my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) endif ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_global_sanitize := $(filter-out integer_overflow,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out integer_overflow,$(my_global_sanitize_diag)) endif # Disable global CFI in excluded paths ifneq ($(filter cfi, $(my_global_sanitize)),) combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \ $(PRODUCT_CFI_EXCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_global_sanitize := $(filter-out cfi,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out cfi,$(my_global_sanitize_diag)) endif endif # Disable global memtag_heap in excluded paths ifneq ($(filter memtag_heap, $(my_global_sanitize)),) combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \ $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_global_sanitize := $(filter-out memtag_heap,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out memtag_heap,$(my_global_sanitize_diag)) endif endif # Disable global HWASan in excluded paths ifneq ($(filter hwaddress, $(my_global_sanitize)),) combined_exclude_paths := $(HWASAN_EXCLUDE_PATHS) \ $(PRODUCT_HWASAN_EXCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_global_sanitize := $(filter-out hwaddress,$(my_global_sanitize)) my_global_sanitize_diag := $(filter-out hwaddress,$(my_global_sanitize_diag)) endif endif ifneq ($(my_global_sanitize),) my_sanitize := $(my_global_sanitize) $(my_sanitize) endif ifneq ($(my_global_sanitize_diag),) my_sanitize_diag := $(my_global_sanitize_diag) $(my_sanitize_diag) endif # The sanitizer specified in the product configuration wins over the previous. ifneq ($(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG),) my_sanitize := $(SANITIZER.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG) ifeq ($(my_sanitize),never) my_sanitize := my_sanitize_diag := endif endif ifndef LOCAL_IS_HOST_MODULE # Add a filter point for 32-bit vs 64-bit sanitization (to lighten the burden) SANITIZE_TARGET_ARCH ?= $(TARGET_ARCH) $(TARGET_2ND_ARCH) ifeq ($(filter $(SANITIZE_TARGET_ARCH),$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) my_sanitize := my_sanitize_diag := endif endif # Add a filter point based on module owner (to lighten the burden). The format is a space- or # colon-separated list of owner names. ifneq (,$(SANITIZE_NEVER_BY_OWNER)) ifneq (,$(LOCAL_MODULE_OWNER)) ifneq (,$(filter $(LOCAL_MODULE_OWNER),$(subst :, ,$(SANITIZE_NEVER_BY_OWNER)))) $(warning Not sanitizing $(LOCAL_MODULE) based on module owner.) my_sanitize := my_sanitize_diag := endif endif endif # Don't apply sanitizers to NDK code. ifdef LOCAL_SDK_VERSION my_sanitize := my_global_sanitize := my_sanitize_diag := endif # Never always wins. ifeq ($(LOCAL_SANITIZE),never) my_sanitize := my_sanitize_diag := endif # Enable CFI in included paths. ifeq ($(filter cfi, $(my_sanitize)),) combined_include_paths := $(CFI_INCLUDE_PATHS) \ $(PRODUCT_CFI_INCLUDE_PATHS) combined_exclude_paths := $(CFI_EXCLUDE_PATHS) \ $(PRODUCT_CFI_EXCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_include_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_sanitize := cfi $(my_sanitize) endif endif endif # Enable memtag_heap in included paths (for Arm64 only). ifeq ($(filter memtag_heap, $(my_sanitize)),) ifneq ($(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) combined_sync_include_paths := $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) \ $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS) combined_async_include_paths := $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) \ $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) combined_exclude_paths := $(MEMTAG_HEAP_EXCLUDE_PATHS) \ $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS) ifneq ($(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS),true) combined_sync_include_paths += $(PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS) combined_async_include_paths += $(PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS) endif ifeq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_exclude_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_sync_include_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_sanitize := memtag_heap $(my_sanitize) my_sanitize_diag := memtag_heap $(my_sanitize_diag) else ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_async_include_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_sanitize := memtag_heap $(my_sanitize) endif endif endif endif # Enable HWASan in included paths. ifeq ($(filter hwaddress, $(my_sanitize)),) combined_include_paths := $(HWASAN_INCLUDE_PATHS) \ $(PRODUCT_HWASAN_INCLUDE_PATHS) ifneq ($(strip $(foreach dir,$(subst $(comma),$(space),$(combined_include_paths)),\ $(filter $(dir)%,$(LOCAL_PATH)))),) my_sanitize := hwaddress $(my_sanitize) endif endif # If CFI is disabled globally, remove it from my_sanitize. ifeq ($(strip $(ENABLE_CFI)),false) my_sanitize := $(filter-out cfi,$(my_sanitize)) my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) endif # Also disable CFI and MTE if ASAN is enabled. ifneq ($(filter address,$(my_sanitize)),) my_sanitize := $(filter-out cfi,$(my_sanitize)) my_sanitize := $(filter-out memtag_stack,$(my_sanitize)) my_sanitize := $(filter-out memtag_globals,$(my_sanitize)) my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) endif # Disable memtag for host targets. Host executables in AndroidMk files are # deprecated, but some partners still have them floating around. ifdef LOCAL_IS_HOST_MODULE my_sanitize := $(filter-out memtag_heap memtag_stack memtag_globals,$(my_sanitize)) my_sanitize_diag := $(filter-out memtag_heap memtag_stack memtag_globals,$(my_sanitize_diag)) endif # Disable sanitizers which need the UBSan runtime for host targets. ifdef LOCAL_IS_HOST_MODULE my_sanitize := $(filter-out cfi,$(my_sanitize)) my_sanitize_diag := $(filter-out cfi,$(my_sanitize_diag)) my_sanitize := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize)) my_sanitize_diag := $(filter-out signed-integer-overflow unsigned-integer-overflow integer_overflow,$(my_sanitize_diag)) endif # Support for local sanitize blacklist paths. ifneq ($(my_sanitize)$(my_global_sanitize),) ifneq ($(LOCAL_SANITIZE_BLOCKLIST),) my_cflags += -fsanitize-blacklist=$(LOCAL_PATH)/$(LOCAL_SANITIZE_BLOCKLIST) endif endif # Disable integer_overflow if LOCAL_NOSANITIZE=integer. ifneq ($(filter integer_overflow, $(my_global_sanitize) $(my_sanitize)),) ifneq ($(filter integer, $(strip $(LOCAL_NOSANITIZE))),) my_sanitize := $(filter-out integer_overflow,$(my_sanitize)) my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag)) endif endif my_nosanitize = $(strip $(LOCAL_NOSANITIZE)) ifneq ($(my_nosanitize),) my_sanitize := $(filter-out $(my_nosanitize),$(my_sanitize)) endif ifneq ($(filter arm x86 x86_64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)),) my_sanitize := $(filter-out hwaddress,$(my_sanitize)) my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) my_sanitize := $(filter-out memtag_stack,$(my_sanitize)) my_sanitize := $(filter-out memtag_globals,$(my_sanitize)) endif ifneq ($(filter hwaddress,$(my_sanitize)),) my_sanitize := $(filter-out address,$(my_sanitize)) my_sanitize := $(filter-out memtag_stack,$(my_sanitize)) my_sanitize := $(filter-out memtag_globals,$(my_sanitize)) my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) my_sanitize := $(filter-out thread,$(my_sanitize)) my_sanitize := $(filter-out cfi,$(my_sanitize)) endif ifneq ($(filter hwaddress,$(my_sanitize)),) my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_RUNTIME_LIBRARY) ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_static_libraries := $(my_static_libraries) \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)HWADDRESS_SANITIZER_STATIC_LIBRARY) \ libdl endif endif endif ifneq ($(filter memtag_heap memtag_stack memtag_globals,$(my_sanitize)),) ifneq ($(filter memtag_heap,$(my_sanitize_diag)),) my_ldflags += -fsanitize-memtag-mode=sync my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag)) else my_ldflags += -fsanitize-memtag-mode=async endif endif # Ignore SANITIZE_TARGET_DIAG=memtag_heap without SANITIZE_TARGET=memtag_heap # This can happen if a condition above filters out memtag_heap from # my_sanitize. It is easier to handle all of these cases here centrally. ifneq ($(filter memtag_heap,$(my_sanitize_diag)),) my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag)) endif ifneq ($(filter memtag_heap,$(my_sanitize)),) my_cflags += -fsanitize=memtag-heap my_ldflags += -fsanitize=memtag-heap my_sanitize := $(filter-out memtag_heap,$(my_sanitize)) endif ifneq ($(filter memtag_stack,$(my_sanitize)),) my_cflags += -fsanitize=memtag-stack my_ldflags += -fsanitize=memtag-stack my_cflags += -Xclang -target-feature -Xclang +mte my_ldflags += -Xclang -target-feature -Xclang +mte my_asflags += -Xclang -target-feature -Xclang +mte my_sanitize := $(filter-out memtag_stack,$(my_sanitize)) endif ifneq ($(filter memtag_globals,$(my_sanitize)),) my_cflags += -fsanitize=memtag-globals my_ldflags += -fsanitize=memtag-globals # TODO(mitchp): For now, enable memtag-heap with memtag-globals because the # linker isn't new enough # (https://reviews.llvm.org/differential/changeset/?ref=4243566). my_sanitize := $(filter-out memtag_globals,$(my_sanitize)) endif # TSAN is not supported on 32-bit architectures. For non-multilib cases, make # its use an error. For multilib cases, don't use it for the 32-bit case. ifneq ($(filter thread,$(my_sanitize)),) ifeq ($(my_32_64_bit_suffix),32) ifeq ($(my_module_multilib),both) my_sanitize := $(filter-out thread,$(my_sanitize)) else $(error $(LOCAL_PATH): $(LOCAL_MODULE): TSAN cannot be used for 32-bit modules.) endif else my_shared_libraries += $(TSAN_RUNTIME_LIBRARY) endif endif ifneq ($(filter safe-stack,$(my_sanitize)),) ifeq ($(my_32_64_bit_suffix),32) my_sanitize := $(filter-out safe-stack,$(my_sanitize)) endif endif # Disable Scudo if ASan or TSan is enabled. ifneq ($(filter address thread hwaddress,$(my_sanitize)),) my_sanitize := $(filter-out scudo,$(my_sanitize)) endif # Or if disabled globally. ifeq ($(PRODUCT_DISABLE_SCUDO),true) my_sanitize := $(filter-out scudo,$(my_sanitize)) endif # Undefined symbols can occur if a non-sanitized library links # sanitized static libraries. That's OK, because the executable # always depends on the ASan runtime library, which defines these # symbols. ifneq ($(filter address thread,$(strip $(SANITIZE_TARGET))),) ifndef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) ifeq ($(my_sanitize),) my_allow_undefined_symbols := true endif endif endif endif ifneq ($(filter default-ub,$(my_sanitize)),) my_sanitize := $(CLANG_DEFAULT_UB_CHECKS) endif ifneq ($(filter fuzzer,$(my_sanitize)),) # SANITIZE_TARGET='fuzzer' actually means to create the fuzzer coverage # information, not to link against the fuzzer main(). my_sanitize := $(filter-out fuzzer,$(my_sanitize)) my_sanitize += fuzzer-no-link # TODO(b/131771163): Disable LTO for fuzzer builds. Note that Cfi causes # dependency on LTO. my_sanitize := $(filter-out cfi,$(my_sanitize)) my_cflags += -fno-lto my_ldflags += -fno-lto # TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries # discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus # doesn't match the linker script due to the "__emutls_v." prefix). my_cflags += -fno-sanitize-coverage=stack-depth my_ldflags += -fno-sanitize-coverage=stack-depth endif ifneq ($(filter integer_overflow,$(my_sanitize)),) # Respect LOCAL_NOSANITIZE for integer-overflow flags. ifeq ($(filter signed-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),) my_sanitize += signed-integer-overflow endif ifeq ($(filter unsigned-integer-overflow, $(strip $(LOCAL_NOSANITIZE))),) my_sanitize += unsigned-integer-overflow endif my_cflags += $(INTEGER_OVERFLOW_EXTRA_CFLAGS) # Check for diagnostics mode. ifneq ($(filter integer_overflow,$(my_sanitize_diag)),) ifneq ($(filter SHARED_LIBRARIES EXECUTABLES,$(LOCAL_MODULE_CLASS)),) ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_sanitize_diag += signed-integer-overflow my_sanitize_diag += unsigned-integer-overflow else $(call pretty-error,Make cannot apply integer overflow diagnostics to static binary.) endif else $(call pretty-error,Make cannot apply integer overflow diagnostics to static library.) endif endif my_sanitize := $(filter-out integer_overflow,$(my_sanitize)) endif # Makes sure integer_overflow diagnostics is removed from the diagnostics list # even if integer_overflow is not set for some reason. ifneq ($(filter integer_overflow,$(my_sanitize_diag)),) my_sanitize_diag := $(filter-out integer_overflow,$(my_sanitize_diag)) endif ifneq ($(my_sanitize),) fsanitize_arg := $(subst $(space),$(comma),$(my_sanitize)) my_cflags += -fsanitize=$(fsanitize_arg) my_asflags += -fsanitize=$(fsanitize_arg) # When fuzzing, we wish to crash with diagnostics on any bug. ifneq ($(filter fuzzer-no-link,$(my_sanitize)),) my_cflags += -fno-sanitize-trap=all my_cflags += -fno-sanitize-recover=all my_ldflags += -fsanitize=fuzzer-no-link else ifdef LOCAL_IS_HOST_MODULE my_cflags += -fno-sanitize-recover=all my_ldflags += -fsanitize=$(fsanitize_arg) else my_cflags += -fsanitize-trap=all ifneq ($(filter address thread,$(my_sanitize)),) my_cflags += -fno-sanitize-trap=address,thread my_shared_libraries += libdl endif endif endif ifneq ($(filter cfi,$(my_sanitize)),) # __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). # LLVM is not set up to do this on a function basis, so force Thumb on the # entire module. LOCAL_ARM_MODE := thumb my_cflags += $(CFI_EXTRA_CFLAGS) my_asflags += $(CFI_EXTRA_ASFLAGS) # Only append the default visibility flag if -fvisibility has not already been # set to hidden. ifeq ($(filter -fvisibility=hidden,$(LOCAL_CFLAGS)),) my_cflags += -fvisibility=default endif my_ldflags += $(CFI_EXTRA_LDFLAGS) ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_ldflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_ldflags)) my_cflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_cflags)) else # Apply the version script to non-static executables my_ldflags += -Wl,--version-script,build/soong/cc/config/cfi_exports.map LOCAL_ADDITIONAL_DEPENDENCIES += build/soong/cc/config/cfi_exports.map endif endif # If local or global modules need ASAN, add linker flags. ifneq ($(filter address,$(my_global_sanitize) $(my_sanitize)),) my_ldflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS) ifdef LOCAL_IS_HOST_MODULE # -nodefaultlibs (provided with libc++) prevents the driver from linking # libraries needed with -fsanitize=address. http://b/18650275 (WAI) my_ldflags += -Wl,--no-as-needed else # Add asan libraries unless LOCAL_MODULE is the asan library. # ASan runtime library must be the first in the link order. ifeq (,$(filter $(LOCAL_MODULE),$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY))) my_shared_libraries := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ $(my_shared_libraries) endif # Do not add unnecessary dependency in shared libraries. ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES) my_ldflags += -Wl,--as-needed endif ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER) # Make sure linker_asan get installed. $(LOCAL_INSTALLED_MODULE) : | $(PRODUCT_OUT)$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER_FILE) endif endif endif endif # If local module needs ASAN, add compiler flags. ifneq ($(filter address,$(my_sanitize)),) # Frame pointer based unwinder in ASan requires ARM frame setup. LOCAL_ARM_MODE := arm my_cflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS) ifndef LOCAL_IS_HOST_MODULE my_cflags += -mllvm -asan-globals=0 endif endif # If local module needs HWASAN, add compiler flags. ifneq ($(filter hwaddress,$(my_sanitize)),) my_cflags += $(HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS) ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) my_linker := /system/bin/linker_hwasan64 endif endif endif # Use minimal diagnostics when integer overflow is enabled; never do it for HOST modules ifeq ($(LOCAL_IS_HOST_MODULE),) # Pre-emptively add UBSAN minimal runtime incase a static library dependency requires it ifeq ($(filter STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)),) ifndef LOCAL_SDK_VERSION my_static_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY) my_ldflags += -Wl,--exclude-libs,$($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_MINIMAL_RUNTIME_LIBRARY).a endif endif ifneq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize)),) ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer,$(my_sanitize_diag)),) ifeq ($(filter cfi,$(my_sanitize_diag)),) ifeq ($(filter address hwaddress fuzzer-no-link,$(my_sanitize)),) my_cflags += -fsanitize-minimal-runtime my_cflags += -fno-sanitize-trap=integer my_cflags += -fno-sanitize-recover=integer endif endif endif endif endif # For Scudo, we opt for the minimal runtime, unless some diagnostics are enabled. ifneq ($(filter scudo,$(my_sanitize)),) ifeq ($(filter unsigned-integer-overflow signed-integer-overflow integer cfi,$(my_sanitize_diag)),) my_cflags += -fsanitize-minimal-runtime endif ifneq ($(filter -fsanitize-minimal-runtime,$(my_cflags)),) my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_MINIMAL_RUNTIME_LIBRARY) else my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)SCUDO_RUNTIME_LIBRARY) endif endif ifneq ($(strip $(LOCAL_SANITIZE_RECOVER)),) recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_RECOVER)), my_cflags += -fsanitize-recover=$(recover_arg) endif ifneq ($(strip $(LOCAL_SANITIZE_NO_RECOVER)),) no_recover_arg := $(subst $(space),$(comma),$(LOCAL_SANITIZE_NO_RECOVER)), my_cflags += -fno-sanitize-recover=$(no_recover_arg) endif ifneq ($(my_sanitize_diag),) # TODO(vishwath): Add diagnostic support for static executables once # we switch to clang-4393122 (which adds the static ubsan runtime # that this depends on) ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) notrap_arg := $(subst $(space),$(comma),$(my_sanitize_diag)), my_cflags += -fno-sanitize-trap=$(notrap_arg) # Diagnostic requires a runtime library, unless ASan or TSan are also enabled. ifeq ($(filter address thread scudo hwaddress,$(my_sanitize)),) # Does not have to be the first DT_NEEDED unlike ASan. my_shared_libraries += $($(LOCAL_2ND_ARCH_VAR_PREFIX)UBSAN_RUNTIME_LIBRARY) endif endif endif # http://b/119329758, Android core does not boot up with this sanitizer yet. # Previously sanitized modules might not pass new implicit-integer-sign-change check. # Disable this check unless it has been explicitly specified. ifneq ($(findstring fsanitize,$(my_cflags)),) ifneq ($(findstring integer,$(my_cflags)),) ifeq ($(findstring sanitize=implicit-integer-sign-change,$(my_cflags)),) my_cflags += -fno-sanitize=implicit-integer-sign-change endif endif endif # http://b/177566116, libc++ may crash with this sanitizer. # Disable this check unless it has been explicitly specified. ifneq ($(findstring fsanitize,$(my_cflags)),) ifneq ($(findstring integer,$(my_cflags)),) ifeq ($(findstring sanitize=unsigned-shift-base,$(my_cflags)),) my_cflags += -fno-sanitize=unsigned-shift-base endif endif endif ================================================ FILE: core/configure_module_stem.mk ================================================ my_multilib_stem := $(LOCAL_MODULE_STEM_$(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32)) ifdef my_multilib_stem my_module_stem := $(my_multilib_stem) $(call verify-module-stem,my_multilib_stem) else ifdef LOCAL_MODULE_STEM my_module_stem := $(LOCAL_MODULE_STEM) $(call verify-module-stem,LOCAL_MODULE_STEM) else my_module_stem := $(LOCAL_MODULE) endif ifdef LOCAL_BUILT_MODULE_STEM my_built_module_stem := $(LOCAL_BUILT_MODULE_STEM) $(call verify-module-stem,LOCAL_BUILT_MODULE_STEM) else my_built_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX) $(call verify-module-stem,LOCAL_MODULE_SUFFIX) endif ifdef LOCAL_INSTALLED_MODULE_STEM my_installed_module_stem := $(LOCAL_INSTALLED_MODULE_STEM) $(call verify-module-stem,LOCAL_INSTALLED_MODULE_STEM) else my_installed_module_stem := $(my_module_stem)$(LOCAL_MODULE_SUFFIX) $(call verify-module-stem,LOCAL_MODULE_SUFFIX) endif ================================================ FILE: core/copy_headers.mk ================================================ ifneq (,$(strip $(LOCAL_COPY_HEADERS))) ########################################################### ## Copy headers to the install tree ########################################################### $(call record-module-type,COPY_HEADERS) ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),) $(call pretty-error,LOCAL_COPY_HEADERS may not be used with host modules) endif # Modules linking against the SDK do not have the include path to use # COPY_HEADERS, so prevent them from exporting any either. ifdef LOCAL_SDK_VERSION $(call pretty-error,Modules using LOCAL_SDK_VERSION may not use LOCAL_COPY_HEADERS) endif include $(BUILD_SYSTEM)/local_vendor_product.mk # Modules in vendor or product may use LOCAL_COPY_HEADERS. # Platform libraries will not have the include path present. ifeq ($(call module-in-vendor-or-product),) $(call pretty-error,Only modules in vendor or product may use LOCAL_COPY_HEADERS) endif # Clean up LOCAL_COPY_HEADERS_TO, since soong_ui will be comparing cleaned # paths to figure out which headers are obsolete and should be removed. LOCAL_COPY_HEADERS_TO := $(call clean-path,$(LOCAL_COPY_HEADERS_TO)) ifneq ($(filter /% .. ../%,$(LOCAL_COPY_HEADERS_TO)),) $(call pretty-error,LOCAL_COPY_HEADERS_TO may not start with / or ../ : $(LOCAL_COPY_HEADERS_TO)) endif ifeq ($(LOCAL_COPY_HEADERS_TO),.) LOCAL_COPY_HEADERS_TO := endif # Create a rule to copy each header, and make the # all_copied_headers phony target depend on each # destination header. copy-one-header defines the # actual rule. # $(foreach header,$(LOCAL_COPY_HEADERS), \ $(eval _chFrom := $(LOCAL_PATH)/$(header)) \ $(eval _chTo := \ $(if $(LOCAL_COPY_HEADERS_TO),\ $(TARGET_OUT_HEADERS)/$(LOCAL_COPY_HEADERS_TO)/$(notdir $(header)),\ $(TARGET_OUT_HEADERS)/$(notdir $(header)))) \ $(eval ALL_COPIED_HEADERS.$(_chTo).MAKEFILE += $(LOCAL_MODULE_MAKEFILE)) \ $(eval ALL_COPIED_HEADERS.$(_chTo).SRC += $(_chFrom)) \ $(if $(filter $(_chTo),$(ALL_COPIED_HEADERS)),, \ $(eval ALL_COPIED_HEADERS += $(_chTo))) \ ) _chFrom := _chTo := $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=COPY_HEADERS)) endif # LOCAL_COPY_HEADERS ================================================ FILE: core/cxx_stl_setup.mk ================================================ ############################################################# ## Set up flags based on LOCAL_CXX_STL. ## Input variables: LOCAL_CXX_STL, my_prefix ## Output variables: My_cflags, my_c_includes, my_shared_libraries, etc. ############################################################# # Select the appropriate C++ STL ifeq ($(strip $(LOCAL_CXX_STL)),default) ifndef LOCAL_SDK_VERSION # Platform code. Select the appropriate STL. my_cxx_stl := libc++ ifdef LOCAL_IS_HOST_MODULE ifneq (,$(BUILD_HOST_static)) my_cxx_stl := libc++_static endif endif else my_cxx_stl := ndk endif else my_cxx_stl := $(strip $(LOCAL_CXX_STL)) ifdef LOCAL_SDK_VERSION # The NDK has historically used LOCAL_NDK_STL_VARIANT to specify the # STL. An Android.mk that specifies both LOCAL_CXX_STL and # LOCAL_SDK_VERSION will incorrectly try (and most likely fail) to use # the platform STL in an NDK binary. Emit an error to direct the user # toward the correct option. # # Note that we could also accept LOCAL_CXX_STL as an alias for # LOCAL_NDK_STL_VARIANT (and in fact soong does use the same name), but # the two options use different names for the STLs. $(error $(LOCAL_PATH): $(LOCAL_MODULE): Must use LOCAL_NDK_STL_VARIANT rather than LOCAL_CXX_STL for NDK binaries) endif endif my_link_type := dynamic ifdef LOCAL_IS_HOST_MODULE ifneq (,$(BUILD_HOST_static)) my_link_type := static endif ifeq (-static,$(filter -static,$(my_ldflags))) my_link_type := static endif else ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE)) my_link_type := static endif endif my_cxx_ldlibs := ifneq ($(filter $(my_cxx_stl),libc++ libc++_static),) ifeq ($($(my_prefix)OS),darwin) # libc++'s headers are annotated with availability macros that indicate # which version of Mac OS was the first to ship with a libc++ feature # available in its *system's* libc++.dylib. We do not use the system's # library, but rather ship our own. As such, these availability # attributes are meaningless for us but cause build breaks when we try # to use code that would not be available in the system's dylib. my_cppflags += -D_LIBCPP_DISABLE_AVAILABILITY endif # Note that the structure of this means that LOCAL_CXX_STL := libc++ will # use the static libc++ for static executables. ifeq ($(my_link_type),dynamic) ifeq ($(my_cxx_stl),libc++) my_shared_libraries += libc++ else my_static_libraries += libc++_static endif else my_static_libraries += libc++_static endif ifdef LOCAL_IS_HOST_MODULE my_cppflags += -nostdinc++ my_ldflags += -nostdlib++ else my_static_libraries += libc++demangle ifeq ($(my_link_type),static) my_static_libraries += libm libc libunwind libstatic_rustlibs_for_make endif endif else ifeq ($(my_cxx_stl),ndk) # Using an NDK STL. Handled in binary.mk. else ifeq ($(my_cxx_stl),libstdc++) $(error $(LOCAL_PATH): $(LOCAL_MODULE): libstdc++ is not supported) else ifeq ($(my_cxx_stl),none) ifdef LOCAL_IS_HOST_MODULE my_cppflags += -nostdinc++ my_ldflags += -nostdlib++ endif else $(error $(LOCAL_PATH): $(LOCAL_MODULE): $(my_cxx_stl) is not a supported STL.) endif ================================================ FILE: core/definitions.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ## ## Common build system definitions. Mostly standard ## commands for building various types of targets, which ## are used by others to construct the final targets. ## # These are variables we use to collect overall lists # of things being processed. # Full paths to all of the documentation ALL_DOCS:= # The short names of all of the targets in the system. # For each element of ALL_MODULES, two other variables # are defined: # $(ALL_MODULES.$(target)).BUILT # $(ALL_MODULES.$(target)).INSTALLED # The BUILT variable contains LOCAL_BUILT_MODULE for that # target, and the INSTALLED variable contains the LOCAL_INSTALLED_MODULE. # Some targets may have multiple files listed in the BUILT and INSTALLED # sub-variables. ALL_MODULES:= ALL_MAKE_MODULE_INFO_JSON_MODULES:= # The relative paths of the non-module targets in the system. ALL_NON_MODULES:= NON_MODULES_WITHOUT_LICENSE_METADATA:= # List of copied targets that need license metadata copied. ALL_COPIED_TARGETS:= # Full paths to targets that should be added to the "make droid" # set of installed targets. ALL_DEFAULT_INSTALLED_MODULES:= # Full path to all asm, C, C++, lex and yacc generated C files. # These all have an order-only dependency on the copied headers ALL_C_CPP_ETC_OBJECTS:= # These files go into the SDK ALL_SDK_FILES:= # Files for dalvik. This is often build without building the rest of the OS. INTERNAL_DALVIK_MODULES:= # All findbugs xml files ALL_FINDBUGS_FILES:= # Packages with certificate violation CERTIFICATE_VIOLATION_MODULES := # Target and host installed module's dependencies on shared libraries. # They are list of "::lib1,lib2...". TARGET_DEPENDENCIES_ON_SHARED_LIBRARIES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_DEPENDENCIES_ON_SHARED_LIBRARIES := HOST_DEPENDENCIES_ON_SHARED_LIBRARIES := $(HOST_2ND_ARCH_VAR_PREFIX)HOST_DEPENDENCIES_ON_SHARED_LIBRARIES := HOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES := $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_DEPENDENCIES_ON_SHARED_LIBRARIES := # Generated class file names for Android resource. # They are escaped and quoted so can be passed safely to a bash command. ANDROID_RESOURCE_GENERATED_CLASSES := 'R.class' 'R$$*.class' 'Manifest.class' 'Manifest$$*.class' # Display names for various build targets TARGET_DISPLAY := target HOST_DISPLAY := host HOST_CROSS_DISPLAY := host cross # All installed initrc files ALL_INIT_RC_INSTALLED_PAIRS := # All installed vintf manifest fragments for a partition at ALL_VINTF_MANIFEST_FRAGMENTS_LIST:= # All compatibility suites mentioned in LOCAL_COMPATIBILITY_SUITE ALL_COMPATIBILITY_SUITES := # All compatibility suite files to dist. ALL_COMPATIBILITY_DIST_FILES := # All LINK_TYPE entries ALL_LINK_TYPES := # All exported/imported include entries EXPORTS_LIST := # All modules already converted to Soong SOONG_ALREADY_CONV := ########################################################### ## Debugging; prints a variable list to stdout ########################################################### # $(1): variable name list, not variable values define print-vars $(foreach var,$(1), \ $(info $(var):) \ $(foreach word,$($(var)), \ $(info $(space)$(space)$(word)) \ ) \ ) endef ########################################################### ## Evaluates to true if the string contains the word true, ## and empty otherwise ## $(1): a var to test ########################################################### define true-or-empty $(filter true, $(1)) endef define boolean-not $(if $(filter true,$(1)),,true) endef ########################################################### ## Rule for touching GCNO files. ########################################################### define gcno-touch-rule $(2): $(1) touch -c $$@ endef ########################################################### ########################################################### ## Retrieve the directory of the current makefile ## Must be called before including any other makefile!! ########################################################### # Figure out where we are. define my-dir $(strip \ $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \ $(if $(filter $(BUILD_SYSTEM)/% $(OUT_DIR)/%,$(LOCAL_MODULE_MAKEFILE)), \ $(error my-dir must be called before including any other makefile.) \ , \ $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \ ) \ ) endef ########################################################### ## Retrieve a list of all makefiles immediately below some directory ########################################################### define all-makefiles-under $(wildcard $(1)/*/Android.mk) endef ########################################################### ## Look under a directory for makefiles that don't have parent ## makefiles. ########################################################### # $(1): directory to search under # Ignores $(1)/Android.mk define first-makefiles-under $(shell build/make/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) \ --mindepth=2 $(addprefix --dir=,$(1)) Android.mk) endef ########################################################### ## Retrieve a list of all makefiles immediately below your directory ## Must be called before including any other makefile!! ########################################################### define all-subdir-makefiles $(call all-makefiles-under,$(call my-dir)) endef ########################################################### ## Look in the named list of directories for makefiles, ## relative to the current directory. ## Must be called before including any other makefile!! ########################################################### # $(1): List of directories to look for under this directory define all-named-subdir-makefiles $(wildcard $(addsuffix /Android.mk, $(addprefix $(call my-dir)/,$(1)))) endef ########################################################### ## Find all of the directories under the named directories with ## the specified name. ## Meant to be used like: ## INC_DIRS := $(call all-named-dirs-under,inc,.) ########################################################### define all-named-dirs-under $(call find-subdir-files,$(2) -type d -name "$(1)") endef ########################################################### ## Find all the directories under the current directory that ## haves name that match $(1) ########################################################### define all-subdir-named-dirs $(call all-named-dirs-under,$(1),.) endef ########################################################### ## Find all of the files under the named directories with ## the specified name. ## Meant to be used like: ## SRC_FILES := $(call all-named-files-under,*.h,src tests) ########################################################### define all-named-files-under $(call find-files-in-subdirs,$(LOCAL_PATH),"$(1)",$(2)) endef ########################################################### ## Find all of the files under the current directory with ## the specified name. ########################################################### define all-subdir-named-files $(call all-named-files-under,$(1),.) endef ########################################################### ## Find all of the java files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-java-files-under,src tests) ########################################################### define all-java-files-under $(call all-named-files-under,*.java,$(1)) endef ########################################################### ## Find all of the java files from here. Meant to be used like: ## SRC_FILES := $(call all-subdir-java-files) ########################################################### define all-subdir-java-files $(call all-java-files-under,.) endef ########################################################### ## Find all of the c files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-c-files-under,src tests) ########################################################### define all-c-files-under $(call all-named-files-under,*.c,$(1)) endef ########################################################### ## Find all of the c files from here. Meant to be used like: ## SRC_FILES := $(call all-subdir-c-files) ########################################################### define all-subdir-c-files $(call all-c-files-under,.) endef ########################################################### ## Find all of the cpp files under the named directories. ## LOCAL_CPP_EXTENSION is respected if set. ## Meant to be used like: ## SRC_FILES := $(call all-cpp-files-under,src tests) ########################################################### define all-cpp-files-under $(sort $(patsubst ./%,%, \ $(shell cd $(LOCAL_PATH) ; \ find -L $(1) -name "*$(or $(LOCAL_CPP_EXTENSION),.cpp)" -and -not -name ".*") \ )) endef ########################################################### ## Find all of the cpp files from here. Meant to be used like: ## SRC_FILES := $(call all-subdir-cpp-files) ########################################################### define all-subdir-cpp-files $(call all-cpp-files-under,.) endef ########################################################### ## Find all files named "I*.aidl" under the named directories, ## which must be relative to $(LOCAL_PATH). The returned list ## is relative to $(LOCAL_PATH). ########################################################### define all-Iaidl-files-under $(call all-named-files-under,I*.aidl,$(1)) endef ########################################################### ## Find all of the "I*.aidl" files under $(LOCAL_PATH). ########################################################### define all-subdir-Iaidl-files $(call all-Iaidl-files-under,.) endef ########################################################### ## Find all files named "*.vts" under the named directories, ## which must be relative to $(LOCAL_PATH). The returned list ## is relative to $(LOCAL_PATH). ########################################################### define all-vts-files-under $(call all-named-files-under,*.vts,$(1)) endef ########################################################### ## Find all of the "*.vts" files under $(LOCAL_PATH). ########################################################### define all-subdir-vts-files $(call all-vts-files-under,.) endef ########################################################### ## Find all of the logtags files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-logtags-files-under,src) ########################################################### define all-logtags-files-under $(call all-named-files-under,*.logtags,$(1)) endef ########################################################### ## Find all of the .proto files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-proto-files-under,src) ########################################################### define all-proto-files-under $(call all-named-files-under,*.proto,$(1)) endef ########################################################### ## Find all of the RenderScript files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-renderscript-files-under,src) ########################################################### define all-renderscript-files-under $(call find-subdir-files,$(1) \( -name "*.rscript" -or -name "*.fs" \) -and -not -name ".*") endef ########################################################### ## Find all of the S files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-c-files-under,src tests) ########################################################### define all-S-files-under $(call all-named-files-under,*.S,$(1)) endef ########################################################### ## Find all of the html files under the named directories. ## Meant to be used like: ## SRC_FILES := $(call all-html-files-under,src tests) ########################################################### define all-html-files-under $(call all-named-files-under,*.html,$(1)) endef ########################################################### ## Find all of the html files from here. Meant to be used like: ## SRC_FILES := $(call all-subdir-html-files) ########################################################### define all-subdir-html-files $(call all-html-files-under,.) endef ########################################################### ## Find all of the files matching pattern ## SRC_FILES := $(call find-subdir-files, ) ########################################################### define find-subdir-files $(sort $(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find -L $(1)))) endef ########################################################### # find the files in the subdirectory $1 of LOCAL_DIR # matching pattern $2, filtering out files $3 # e.g. # SRC_FILES += $(call find-subdir-subdir-files, \ # css, *.cpp, DontWantThis.cpp) ########################################################### define find-subdir-subdir-files $(sort $(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \ $(LOCAL_PATH) ; find -L $(1) -maxdepth 1 -name $(2))))) endef ########################################################### ## Find all of the files matching pattern ## SRC_FILES := $(call all-subdir-java-files) ########################################################### define find-subdir-assets $(sort $(if $(1),$(patsubst ./%,%, \ $(shell if [ -d $(1) ] ; then cd $(1) ; find -L ./ -not -name '.*' -and -type f ; fi)), \ $(warning Empty argument supplied to find-subdir-assets in $(LOCAL_PATH)) \ )) endef ########################################################### ## Find various file types in a list of directories relative to $(LOCAL_PATH) ########################################################### define find-other-java-files $(call all-java-files-under,$(1)) endef define find-other-html-files $(call all-html-files-under,$(1)) endef ########################################################### # Use utility find to find given files in the given subdirs. # This function uses $(1), instead of LOCAL_PATH as the base. # $(1): the base dir, relative to the root of the source tree. # $(2): the file name pattern to be passed to find as "-name". # $(3): a list of subdirs of the base dir. # Returns: a list of paths relative to the base dir. ########################################################### define find-files-in-subdirs $(sort $(patsubst ./%,%, \ $(shell cd $(1) ; \ find -L $(3) -name $(2) -and -not -name ".*") \ )) endef ########################################################### ## Scan through each directory of $(1) looking for files ## that match $(2) using $(wildcard). Useful for seeing if ## a given directory or one of its parents contains ## a particular file. Returns the first match found, ## starting furthest from the root. ########################################################### define find-parent-file $(strip \ $(eval _fpf := $(sort $(wildcard $(foreach f, $(2), $(strip $(1))/$(f))))) \ $(if $(_fpf),$(_fpf), \ $(if $(filter-out ./ .,$(1)), \ $(call find-parent-file,$(patsubst %/,%,$(dir $(1))),$(2)) \ ) \ ) \ ) endef ########################################################### ## Find test data in a form required by LOCAL_TEST_DATA ## $(1): the base dir, relative to the root of the source tree. ## $(2): the file name pattern to be passed to find as "-name" ## $(3): a list of subdirs of the base dir ########################################################### define find-test-data-in-subdirs $(foreach f,$(sort $(patsubst ./%,%, \ $(shell cd $(1) ; \ find -L $(3) -type f -and -name $(2) -and -not -name ".*") \ )),$(1):$(f)) endef ########################################################### ## Function we can evaluate to introduce a dynamic dependency ########################################################### define add-dependency $(1): $(2) endef ########################################################### ## Reverse order of a list ########################################################### define reverse-list $(if $(1),$(call reverse-list,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) endef ########################################################### ## Sometimes a notice dependency will reference an unadorned ## module name that only appears in ALL_MODULES adorned with ## an ARCH suffix or a `host_cross_` prefix. ## ## After all of the modules are processed in base_rules.mk, ## replace all such dependencies with every matching adorned ## module name. ########################################################### define fix-notice-deps $(strip \ $(eval _all_module_refs := \ $(sort \ $(foreach m,$(sort $(ALL_MODULES)), \ $(call word-colon,1,$(ALL_MODULES.$(m).NOTICE_DEPS)) \ ) \ ) \ ) \ $(foreach m, $(_all_module_refs), \ $(eval _lookup.$(m) := \ $(sort \ $(if $(strip $(ALL_MODULES.$(m).PATH)), \ $(m), \ $(filter $(m)_32 $(m)_64 host_cross_$(m) host_cross_$(m)_32 host_cross_$(m)_64, $(ALL_MODULES)) \ ) \ ) \ ) \ ) \ $(foreach m, $(ALL_MODULES), \ $(eval ALL_MODULES.$(m).NOTICE_DEPS := \ $(sort \ $(foreach d,$(sort $(ALL_MODULES.$(m).NOTICE_DEPS)), \ $(foreach n,$(_lookup.$(call word-colon,1,$(d))),$(n):$(call wordlist-colon,2,9999,$(d))) \ ) \ ) \ ) \ ) \ ) endef ########################################################### ## Target directory for license metadata files. ########################################################### define license-metadata-dir $(call generated-sources-dir-for,META,lic,$(filter-out $(PRODUCT_OUT)%,$(1))) endef TARGETS_MISSING_LICENSE_METADATA:= ########################################################### # License metadata targets corresponding to targets in $(1) ########################################################### define corresponding-license-metadata $(strip $(filter-out 0p,$(foreach target, $(sort $(1)), \ $(if $(strip $(ALL_MODULES.$(target).META_LIC)), \ $(ALL_MODULES.$(target).META_LIC), \ $(if $(strip $(ALL_TARGETS.$(target).META_LIC)), \ $(ALL_TARGETS.$(target).META_LIC), \ $(eval TARGETS_MISSING_LICENSE_METADATA += $(target)) \ ) \ ) \ ))) endef ########################################################### ## Record a target $(1) copied from another target(s) $(2) that will need ## license metadata. ########################################################### define declare-copy-target-license-metadata $(strip $(if $(filter $(OUT_DIR)%,$(2)),\ $(eval _tgt:=$(strip $(1)))\ $(eval ALL_COPIED_TARGETS.$(_tgt).SOURCES := $(sort $(ALL_COPIED_TARGETS.$(_tgt).SOURCES) $(filter $(OUT_DIR)%,$(2))))\ $(eval ALL_COPIED_TARGETS += $(_tgt)))) endef ########################################################### ## License metadata build rule for my_register_name $(1) ########################################################### define license-metadata-rule $(foreach meta_lic, $(ALL_MODULES.$(1).DELAYED_META_LIC),$(call _license-metadata-rule,$(1),$(meta_lic))) endef $(KATI_obsolete_var notice-rule, This function has been removed) define _license-metadata-rule $(strip $(eval _srcs := $(strip $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED)), $(ALL_MODULES.$(call word-colon,1,$(d)).INSTALLED),$(if $(strip $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT)), $(ALL_MODULES.$(call word-colon,1,$(d)).BUILT), $(call word-colon,1,$d))))))) $(strip $(eval _deps := $(sort $(filter-out $(2)%,\ $(foreach d,$(ALL_MODULES.$(1).NOTICE_DEPS),\ $(addsuffix :$(call wordlist-colon,2,9999,$(d)), \ $(foreach dt,$(ALL_MODULES.$(d).BUILT) $(ALL_MODULES.$(d).INSTALLED),\ $(ALL_TARGETS.$(dt).META_LIC)))))))) $(strip $(eval _notices := $(sort $(ALL_MODULES.$(1).NOTICES)))) $(strip $(eval _tgts := $(sort $(ALL_MODULES.$(1).BUILT)))) $(strip $(eval _inst := $(sort $(ALL_MODULES.$(1).INSTALLED)))) $(strip $(eval _path := $(sort $(ALL_MODULES.$(1).PATH)))) $(strip $(eval _map := $(strip $(foreach _m,$(sort $(ALL_MODULES.$(1).LICENSE_INSTALL_MAP)), \ $(eval _s := $(call word-colon,1,$(_m))) \ $(eval _d := $(call word-colon,2,$(_m))) \ $(eval _ns := $(if $(strip $(ALL_MODULES.$(_s).INSTALLED)),$(ALL_MODULES.$(_s).INSTALLED),$(if $(strip $(ALL_MODULES.$(_s).BUILT)),$(ALL_MODULES.$(_s).BUILT),$(_s)))) \ $(foreach ns,$(_ns),$(ns):$(_d) ) \ )))) $(2): PRIVATE_KINDS := $(sort $(ALL_MODULES.$(1).LICENSE_KINDS)) $(2): PRIVATE_CONDITIONS := $(sort $(ALL_MODULES.$(1).LICENSE_CONDITIONS)) $(2): PRIVATE_NOTICES := $(_notices) $(2): PRIVATE_NOTICE_DEPS := $(_deps) $(2): PRIVATE_SOURCES := $(_srcs) $(2): PRIVATE_TARGETS := $(_tgts) $(2): PRIVATE_INSTALLED := $(_inst) $(2): PRIVATE_PATH := $(_path) $(2): PRIVATE_IS_CONTAINER := $(ALL_MODULES.$(1).IS_CONTAINER) $(2): PRIVATE_PACKAGE_NAME := $(strip $(ALL_MODULES.$(1).LICENSE_PACKAGE_NAME)) $(2): PRIVATE_INSTALL_MAP := $(_map) $(2): PRIVATE_MODULE_NAME := $(1) $(2): PRIVATE_MODULE_TYPE := $(ALL_MODULES.$(1).MODULE_TYPE) $(2): PRIVATE_MODULE_CLASS := $(ALL_MODULES.$(1).MODULE_CLASS) $(2): PRIVATE_INSTALL_MAP := $(_map) $(2): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(2)/arguments $(2): $(BUILD_LICENSE_METADATA) $(2) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) ) rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(call dump-words-to-file,\ $$(addprefix -mn ,$$(PRIVATE_MODULE_NAME))\ $$(addprefix -mt ,$$(PRIVATE_MODULE_TYPE))\ $$(addprefix -mc ,$$(PRIVATE_MODULE_CLASS))\ $$(addprefix -k ,$$(PRIVATE_KINDS))\ $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\ $$(addprefix -n ,$$(PRIVATE_NOTICES))\ $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\ $$(addprefix -s ,$$(PRIVATE_SOURCES))\ $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\ $$(addprefix -t ,$$(PRIVATE_TARGETS))\ $$(addprefix -i ,$$(PRIVATE_INSTALLED))\ $$(addprefix -r ,$$(PRIVATE_PATH)),\ $$(PRIVATE_ARGUMENT_FILE)) OUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \ $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \ -p '$$(PRIVATE_PACKAGE_NAME)' \ @$$(PRIVATE_ARGUMENT_FILE) \ -o $$@ endef ########################################################### ## License metadata build rule for non-module target $(1) ########################################################### define non-module-license-metadata-rule $(strip $(eval _dir := $(call license-metadata-dir,$(1)))) $(strip $(eval _tgt := $(strip $(1)))) $(strip $(eval _meta := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(_tgt).meta_lic)))) $(strip $(eval _deps := $(sort $(filter-out 0p: :,$(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)),$(ALL_TARGETS.$(call word-colon,1,$(d)).META_LIC):$(call wordlist-colon,2,9999,$(d))))))) $(strip $(eval _notices := $(sort $(ALL_NON_MODULES.$(_tgt).NOTICES)))) $(strip $(eval _path := $(sort $(ALL_NON_MODULES.$(_tgt).PATH)))) $(strip $(eval _install_map := $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS))) $(_meta): PRIVATE_KINDS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_KINDS)) $(_meta): PRIVATE_CONDITIONS := $(sort $(ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS)) $(_meta): PRIVATE_NOTICES := $(_notices) $(_meta): PRIVATE_NOTICE_DEPS := $(_deps) $(_meta): PRIVATE_SOURCES := $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(_meta): PRIVATE_TARGETS := $(_tgt) $(_meta): PRIVATE_PATH := $(_path) $(_meta): PRIVATE_IS_CONTAINER := $(ALL_NON_MODULES.$(_tgt).IS_CONTAINER) $(_meta): PRIVATE_PACKAGE_NAME := $(strip $(ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME)) $(_meta): PRIVATE_INSTALL_MAP := $(strip $(_install_map)) $(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,notice)/$(_meta)/arguments $(_meta): $(BUILD_LICENSE_METADATA) $(_meta) : $(foreach d,$(_deps),$(call word-colon,1,$(d))) $(foreach n,$(_notices),$(call word-colon,1,$(n)) ) rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(call dump-words-to-file,\ $$(addprefix -k ,$$(PRIVATE_KINDS))\ $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\ $$(addprefix -n ,$$(PRIVATE_NOTICES))\ $$(addprefix -d ,$$(PRIVATE_NOTICE_DEPS))\ $$(addprefix -s ,$$(PRIVATE_SOURCES))\ $$(addprefix -m ,$$(PRIVATE_INSTALL_MAP))\ $$(addprefix -t ,$$(PRIVATE_TARGETS))\ $$(addprefix -r ,$$(PRIVATE_PATH)),\ $$(PRIVATE_ARGUMENT_FILE)) OUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \ -mt raw -mc unknown \ $$(if $$(PRIVATE_IS_CONTAINER),-is_container) \ $$(addprefix -r ,$$(PRIVATE_PATH)) \ @$$(PRIVATE_ARGUMENT_FILE) \ -o $$@ endef ########################################################### ## Record missing dependencies for non-module target $(1) ########################################################### define record-missing-non-module-dependencies $(strip $(eval _tgt := $(strip $(1)))) $(strip $(foreach d,$(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES)), \ $(if $(strip $(ALL_TARGETS.$(d).META_LIC)), \ , \ $(eval NON_MODULES_WITHOUT_LICENSE_METADATA += $(d))) \ )) endef ########################################################### ## License metadata build rule for copied target $(1) ########################################################### define copied-target-license-metadata-rule $(if $(strip $(ALL_TARGETS.$(1).META_LIC)),,$(call _copied-target-license-metadata-rule,$(1))) endef define _copied-target-license-metadata-rule $(strip $(eval _dir := $(call license-metadata-dir,$(1)))) $(strip $(eval _meta := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(1).meta_lic)))) $(strip $(eval ALL_TARGETS.$(1).META_LIC:=$(_meta))) $(strip $(eval _dep:=)) $(strip $(foreach s,$(ALL_COPIED_TARGETS.$(1).SOURCES),\ $(eval _dmeta:=$(ALL_TARGETS.$(s).META_LIC))\ $(if $(filter-out 0p,$(_dep)),\ $(if $(filter-out $(_dep),$(_dmeta)),$(error cannot copy target from multiple modules: $(1) from $(_dep) and $(_dmeta))),\ $(eval _dep:=$(_dmeta))))) $(if $(filter 0p,$(_dep)),$(eval ALL_TARGETS.$(1).META_LIC:=0p)) $(strip $(if $(strip $(_dep)),,$(error cannot copy target from unknown module: $(1) from $(ALL_COPIED_TARGETS.$(1).SOURCES)))) ifneq (0p,$(ALL_TARGETS.$(1).META_LIC)) $(_meta): PRIVATE_DEST_TARGET := $(1) $(_meta): PRIVATE_SOURCE_TARGETS := $(ALL_COPIED_TARGETS.$(1).SOURCES) $(_meta): PRIVATE_SOURCE_METADATA := $(_dep) $(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,copynotice)/$(_meta)/arguments $(_meta) : $(_dep) $(COPY_LICENSE_METADATA) rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(call dump-words-to-file,\ $$(addprefix -i ,$$(PRIVATE_DEST_TARGET))\ $$(addprefix -s ,$$(PRIVATE_SOURCE_TARGETS))\ $$(addprefix -d ,$$(PRIVATE_SOURCE_METADATA)),\ $$(PRIVATE_ARGUMENT_FILE)) OUT_DIR=$(OUT_DIR) $(COPY_LICENSE_METADATA) \ @$$(PRIVATE_ARGUMENT_FILE) \ -o $$@ endif $(eval _dep:=) $(eval _dmeta:=) $(eval _meta:=) $(eval _dir:=) endef ########################################################### ## Declare the license metadata for non-module target $(1). ## ## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 ## $(3) -- license conditions e.g. notice by_exception_only ## $(4) -- license text filenames (notices) ## $(5) -- package name ## $(6) -- project path ########################################################### define declare-license-metadata $(strip \ $(eval _tgt := $(subst //,/,$(strip $(1)))) \ $(eval ALL_NON_MODULES += $(_tgt)) \ $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \ $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \ $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \ ) endef ########################################################### ## Declare that non-module targets copied from project $(1) and ## optionally ending in $(2) have the following license ## metadata: ## ## $(3) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 ## $(4) -- license conditions e.g. notice by_exception_only ## $(5) -- license text filenames (notices) ## $(6) -- package name ########################################################### define declare-copy-files-license-metadata $(strip \ $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(eval $(call declare-license-metadata,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(3),$(4),$(5),$(6),$(1)))) \ ) endef ########################################################### ## Declare the license metadata for non-module container-type target $(1). ## ## Container-type targets are targets like .zip files that ## merely aggregate other files. ## ## $(2) -- license kinds e.g. SPDX-license-identifier-Apache-2.0 ## $(3) -- license conditions e.g. notice by_exception_only ## $(4) -- license text filenames (notices) ## $(5) -- package name ## $(6) -- project path ########################################################### define declare-container-license-metadata $(strip \ $(eval _tgt := $(subst //,/,$(strip $(1)))) \ $(eval ALL_NON_MODULES += $(_tgt)) \ $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \ $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \ $(eval ALL_NON_MODULES.$(_tgt).LICENSE_PACKAGE_NAME := $(strip $(5))) \ $(eval ALL_NON_MODULES.$(_tgt).PATH := $(strip $(6))) \ $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \ ) endef ########################################################### ## Declare that non-module target $(1) is a non-copyrightable file. ## ## e.g. an information-only file merely listing other files. ########################################################### define declare-0p-target $(strip \ $(eval _tgt := $(subst //,/,$(strip $(1)))) \ $(eval ALL_0P_TARGETS += $(_tgt)) \ ) endef ########################################################### ## Declare non-module target $(1) to have a first-party license ## (Android Apache 2.0) ## ## $(2) -- project path ########################################################### define declare-1p-target $(call declare-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2)) endef ########################################################### ## Declare that non-module targets copied from project $(1) and ## optionally ending in $(2) are first-party licensed ## (Android Apache 2.0) ########################################################### define declare-1p-copy-files $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(call declare-1p-target,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair)),$(1))) endef ########################################################### ## Declare non-module container-type target $(1) to have a ## first-party license (Android Apache 2.0). ## ## Container-type targets are targets like .zip files that ## merely aggregate other files. ## ## $92) -- project path ########################################################### define declare-1p-container $(call declare-container-license-metadata,$(1),SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,Android,$(2)) endef ########################################################### ## Declare license dependencies $(2) with optional colon-separated ## annotations for non-module target $(1) ########################################################### define declare-license-deps $(strip \ $(eval _tgt := $(subst //,/,$(strip $(1)))) \ $(eval ALL_NON_MODULES += $(_tgt)) \ $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \ $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \ ) endef ########################################################### ## Declare license dependencies $(2) with optional colon-separated ## annotations for non-module container-type target $(1) ## ## Container-type targets are targets like .zip files that ## merely aggregate other files. ## ## $(3) -- root mappings space-separated source:target ########################################################### define declare-container-license-deps $(strip \ $(eval _tgt := $(subst //,/,$(strip $(1)))) \ $(eval ALL_NON_MODULES += $(_tgt)) \ $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir,$(1))/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \ $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \ $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \ $(eval ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS := $(strip $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS) $(3))) \ ) endef ########################################################### ## Declares the rule to report targets with no license metadata. ########################################################### define report-missing-licenses-rule .PHONY: reportmissinglicenses reportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA)) reportmissinglicenses: PRIVATE_COPIED_FILES:=$(sort $(filter $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA),\ $(foreach _pair,$(PRODUCT_COPY_FILES), $(PRODUCT_OUT)/$(call word-colon,2,$(_pair))))) reportmissinglicenses: @echo Reporting $$(words $$(PRIVATE_NON_MODULES)) targets without license metadata $$(foreach t,$$(PRIVATE_NON_MODULES),if ! [ -h $$(t) ]; then echo No license metadata for $$(t) >&2; fi;) $$(foreach t,$$(PRIVATE_COPIED_FILES),if ! [ -h $$(t) ]; then echo No license metadata for copied file $$(t) >&2; fi;) echo $$(words $$(PRIVATE_NON_MODULES)) targets missing license metadata >&2 endef ########################################################### # Returns the unique list of built license metadata files. ########################################################### define all-license-metadata $(sort \ $(foreach t,$(ALL_NON_MODULES),$(if $(filter 0p,$(ALL_TARGETS.$(t).META_LIC)),, $(ALL_TARGETS.$(t).META_LIC))) \ $(foreach m,$(ALL_MODULES), $(ALL_MODULES.$(m).META_LIC)) \ ) endef ########################################################### # Declares the rule to report all library names used in any notice files. ########################################################### define report-all-notice-library-names-rule $(strip $(eval _all := $(call all-license-metadata))) .PHONY: reportallnoticelibrarynames reportallnoticelibrarynames: PRIVATE_LIST_FILE := $(call license-metadata-dir,COMMON)/filelist reportallnoticelibrarynames: | $(COMPLIANCENOTICE_SHIPPEDLIBS) reportallnoticelibrarynames: $(_all) @echo Reporting notice library names for at least $$(words $(_all)) license metadata files $(hide) rm -f $$(PRIVATE_LIST_FILE) $(hide) mkdir -p $$(dir $$(PRIVATE_LIST_FILE)) $(hide) find out -name '*meta_lic' -type f -printf '"%p"\n' >$$(PRIVATE_LIST_FILE) OUT_DIR=$(OUT_DIR) $(COMPLIANCENOTICE_SHIPPEDLIBS) @$$(PRIVATE_LIST_FILE) endef ########################################################### # Declares the rule to build all license metadata. ########################################################### define build-all-license-metadata-rule $(strip $(eval _all := $(call all-license-metadata))) .PHONY: alllicensemetadata alllicensemetadata: $(_all) @echo Building all $(words $(_all)) license metadata files endef ########################################################### ## Declares a license metadata build rule for ALL_MODULES ########################################################### define build-license-metadata $(strip \ $(foreach t,$(sort $(ALL_0P_TARGETS)), \ $(eval ALL_TARGETS.$(t).META_LIC := 0p) \ ) \ $(foreach t,$(sort $(ALL_COPIED_TARGETS)),$(eval $(call copied-target-license-metadata-rule,$(t)))) \ $(foreach t,$(sort $(ALL_NON_MODULES)),$(eval $(call non-module-license-metadata-rule,$(t)))) \ $(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m)))) \ $(eval $(call build-all-license-metadata-rule))) endef ########################################################### ## Returns correct _idfPrefix from the list: ## { HOST, HOST_CROSS, TARGET } ########################################################### # the following rules checked in order: # ($1 is in {HOST_CROSS} => $1; # ($1 is empty) => TARGET; # ($2 is not empty) => HOST_CROSS; # => HOST; define find-idf-prefix $(strip \ $(eval _idf_pfx_:=$(strip $(filter HOST_CROSS,$(1)))) \ $(eval _idf_pfx_:=$(if $(strip $(1)),$(if $(_idf_pfx_),$(_idf_pfx_),$(if $(strip $(2)),HOST_CROSS,HOST)),TARGET)) \ $(_idf_pfx_) ) endef ########################################################### ## The intermediates directory. Where object files go for ## a given target. We could technically get away without ## the "_intermediates" suffix on the directory, but it's ## nice to be able to grep for that string to find out if ## anyone's abusing the system. ########################################################### # $(1): target class, like "APPS" # $(2): target name, like "NotePad" # $(3): { HOST, HOST_CROSS, , } # $(4): if non-empty, force the intermediates to be COMMON # $(5): if non-empty, force the intermediates to be for the 2nd arch # $(6): if non-empty, force the intermediates to be for the host cross os define intermediates-dir-for $(strip \ $(eval _idfClass := $(strip $(1))) \ $(if $(_idfClass),, \ $(error $(LOCAL_PATH): Class not defined in call to intermediates-dir-for)) \ $(eval _idfName := $(strip $(2))) \ $(if $(_idfName),, \ $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \ $(eval _idfPrefix := $(call find-idf-prefix,$(3),$(6))) \ $(eval _idf2ndArchPrefix := $(if $(strip $(5)),$(TARGET_2ND_ARCH_VAR_PREFIX))) \ $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \ $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \ ,$(if $(filter $(_idfClass),$(PER_ARCH_MODULE_CLASSES)),\ $(eval _idfIntBase := $($(_idf2ndArchPrefix)$(_idfPrefix)_OUT_INTERMEDIATES)) \ ,$(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES)) \ ) \ ) \ $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \ ) endef # Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE # to determine the intermediates directory. # # $(1): if non-empty, force the intermediates to be COMMON # $(2): if non-empty, force the intermediates to be for the 2nd arch # $(3): if non-empty, force the intermediates to be for the host cross os define local-intermediates-dir $(strip \ $(if $(strip $(LOCAL_MODULE_CLASS)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \ $(if $(strip $(LOCAL_MODULE)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \ $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \ ) endef # Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE # to determine the intermediates directory. # # $(1): if non-empty, force the intermediates to be COMMON # $(2): if non-empty, force the intermediates to be for the 2nd arch # $(3): if non-empty, force the intermediates to be for the host cross os define local-meta-intermediates-dir $(strip \ $(if $(strip $(LOCAL_MODULE_CLASS)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-meta-intermediates-dir)) \ $(if $(strip $(LOCAL_MODULE)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-meta-intermediates-dir)) \ $(call intermediates-dir-for,META$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \ ) endef ########################################################### ## The generated sources directory. Placing generated ## source files directly in the intermediates directory ## causes problems for multiarch builds, where there are ## two intermediates directories for a single target. Put ## them in a separate directory, and they will be copied to ## each intermediates directory automatically. ########################################################### # $(1): target class, like "APPS" # $(2): target name, like "NotePad" # $(3): { HOST, HOST_CROSS, , } # $(4): if non-empty, force the generated sources to be COMMON define generated-sources-dir-for $(strip \ $(eval _idfClass := $(strip $(1))) \ $(if $(_idfClass),, \ $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \ $(eval _idfName := $(strip $(2))) \ $(if $(_idfName),, \ $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \ $(eval _idfPrefix := $(call find-idf-prefix,$(3),)) \ $(if $(filter $(_idfPrefix)_$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \ $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_GEN)) \ , \ $(eval _idfIntBase := $($(_idfPrefix)_OUT_GEN)) \ ) \ $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \ ) endef # Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE # to determine the generated sources directory. # # $(1): if non-empty, force the intermediates to be COMMON define local-generated-sources-dir $(strip \ $(if $(strip $(LOCAL_MODULE_CLASS)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \ $(if $(strip $(LOCAL_MODULE)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \ $(call generated-sources-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1)) \ ) endef ########################################################### ## The packaging directory for a module. Similar to intermedates, but ## in a location that will be wiped by an m installclean. ########################################################### # $(1): subdir in PACKAGING # $(2): target class, like "APPS" # $(3): target name, like "NotePad" # $(4): { HOST, HOST_CROSS, , } define packaging-dir-for $(strip \ $(eval _pdfClass := $(strip $(2))) \ $(if $(_pdfClass),, \ $(error $(LOCAL_PATH): Class not defined in call to generated-sources-dir-for)) \ $(eval _pdfName := $(strip $(3))) \ $(if $(_pdfName),, \ $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \ $(call intermediates-dir-for,PACKAGING,$(1),$(4))/$(_pdfClass)/$(_pdfName)_intermediates \ ) endef # Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE # to determine the packaging directory. # # $(1): subdir in PACKAGING define local-packaging-dir $(strip \ $(if $(strip $(LOCAL_MODULE_CLASS)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \ $(if $(strip $(LOCAL_MODULE)),, \ $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \ $(call packaging-dir-for,$(1),$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST)) \ ) endef ########################################################### ## Convert a list of short module names (e.g., "framework", "Browser") ## into the list of files that are built for those modules. ## NOTE: this won't return reliable results until after all ## sub-makefiles have been included. ## $(1): target list ########################################################### define module-built-files $(foreach module,$(1),$(ALL_MODULES.$(module).BUILT)) endef ########################################################### ## Convert a list of short modules names (e.g., "framework", "Browser") ## into the list of files that are installed for those modules. ## NOTE: this won't return reliable results until after all ## sub-makefiles have been included. ## $(1): target list ########################################################### define module-installed-files $(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED)) endef ########################################################### ## Convert a list of short modules names (e.g., "framework", "Browser") ## into the list of files that are built *for the target* for those modules. ## NOTE: this won't return reliable results until after all ## sub-makefiles have been included. ## $(1): target list ########################################################### define module-target-built-files $(foreach module,$(1),$(ALL_MODULES.$(module).TARGET_BUILT)) endef ########################################################### ## Convert a list of short modules names (e.g., "framework", "Browser") ## into the list of files that should be used when linking ## against that module as a public API. ## TODO: Allow this for more than JAVA_LIBRARIES modules ## NOTE: this won't return reliable results until after all ## sub-makefiles have been included. ## $(1): target list ########################################################### define module-stubs-files $(foreach module,$(1),$(if $(filter $(module),$(JAVA_SDK_LIBRARIES)),\ $(call java-lib-files,$(module).stubs),$(ALL_MODULES.$(module).STUBS))) endef ########################################################### ## Evaluates to the timestamp file for a doc module, which ## is the dependency that should be used. ## $(1): doc module ########################################################### define doc-timestamp-for $(OUT_DOCS)/$(strip $(1))-timestamp endef ########################################################### ## Convert "core ext framework" to "out/.../javalib.jar ..." ## $(1): library list ## $(2): Non-empty if IS_HOST_MODULE ########################################################### # Get the jar files (you can pass to "javac -classpath") of static or shared # Java libraries that you want to link against. # $(1): library name list # $(2): Non-empty if IS_HOST_MODULE define java-lib-files $(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes.jar) endef # Get the header jar files (you can pass to "javac -classpath") of static or shared # Java libraries that you want to link against. # $(1): library name list # $(2): Non-empty if IS_HOST_MODULE ifneq ($(TURBINE_ENABLED),false) define java-lib-header-files $(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),$(2),COMMON)/classes-header.jar) endef else define java-lib-header-files $(call java-lib-files,$(1),$(2)) endef endif # Get the dependency files (you can put on the right side of "|" of a build rule) # of the Java libraries. # $(1): library name list # $(2): Non-empty if IS_HOST_MODULE # Historically for target Java libraries we used a different file (javalib.jar) # as the dependency. # Now we can use classes.jar as dependency, so java-lib-deps is the same # as java-lib-files. define java-lib-deps $(call java-lib-files,$(1),$(2)) endef # Get the jar files (you can pass to "javac -classpath") of static or shared # APK libraries that you want to link against. # $(1): library name list define app-lib-files $(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes.jar) endef # Get the header jar files (you can pass to "javac -classpath") of static or shared # APK libraries that you want to link against. # $(1): library name list ifneq ($(TURBINE_ENABLED),false) define app-lib-header-files $(foreach lib,$(1),$(call intermediates-dir-for,APPS,$(lib),,COMMON)/classes-header.jar) endef else define app-lib-header-files $(call app-lib-files,$(1)) endef endif # Get the exported-sdk-libs files which collectively give you the list of exported java sdk # lib names that are (transitively) exported from the given set of java libs # $(1): library name list define exported-sdk-libs-files $(foreach lib,$(1),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/exported-sdk-libs) endef ########################################################### ## Append a leaf to a base path. Properly deals with ## base paths ending in /. ## ## $(1): base path ## $(2): leaf path ########################################################### define append-path $(subst //,/,$(1)/$(2)) endef ########################################################### ## Color-coded warnings and errors ## Use echo-(warning|error) in a build rule ## Use pretty-(warning|error) instead of $(warning)/$(error) ########################################################### ESC_BOLD := \033[1m ESC_WARNING := \033[35m ESC_ERROR := \033[31m ESC_RESET := \033[0m # $(1): path (and optionally line) information # $(2): message to print define echo-warning echo -e "$(ESC_BOLD)$(1): $(ESC_WARNING)warning:$(ESC_RESET)$(ESC_BOLD)" '$(subst ','\'',$(2))' "$(ESC_RESET)" >&2 endef # $(1): path (and optionally line) information # $(2): message to print define echo-error echo -e "$(ESC_BOLD)$(1): $(ESC_ERROR)error:$(ESC_RESET)$(ESC_BOLD)" '$(subst ','\'',$(2))' "$(ESC_RESET)" >&2 endef ########################################################### ## Legacy showcommands compatibility ########################################################### define pretty @echo $1 endef ########################################################### ## Commands for including the dependency files the compiler generates ########################################################### # $(1): the .P file # $(2): the main build target define include-depfile $(eval $(2) : .KATI_DEPFILE := $1) endef # $(1): object files define include-depfiles-for-objs $(foreach obj, $(1), $(call include-depfile, $(obj:%.o=%.d), $(obj))) endef ########################################################### ## Track source files compiled to objects ########################################################### # $(1): list of sources # $(2): list of matching objects define track-src-file-obj $(eval $(call _track-src-file-obj,$(1))) endef define _track-src-file-obj i := w $(foreach s,$(1), my_tracked_src_files += $(s) my_src_file_obj_$(s) := $$(word $$(words $$(i)),$$(2)) i += w) endef # $(1): list of sources # $(2): list of matching generated sources define track-src-file-gen $(eval $(call _track-src-file-gen,$(2))) endef define _track-src-file-gen i := w $(foreach s,$(1), my_tracked_gen_files += $(s) my_src_file_gen_$(s) := $$(word $$(words $$(i)),$$(1)) i += w) endef # $(1): list of generated sources # $(2): list of matching objects define track-gen-file-obj $(call track-src-file-obj,$(foreach f,$(1),\ $(or $(my_src_file_gen_$(f)),$(f))),$(2)) endef ########################################################### ## Commands for running lex ########################################################### define transform-l-to-c-or-cpp @echo "Lex: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) M4=$(M4) $(LEX) -o$@ $< endef ########################################################### ## Commands for running yacc ## ########################################################### define transform-y-to-c-or-cpp @echo "Yacc: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) M4=$(M4) $(YACC) $(PRIVATE_YACCFLAGS) \ --defines=$(basename $@).h \ -o $@ $< endef ########################################################### ## Commands to compile RenderScript to Java ########################################################### ## Merge multiple .d files generated by llvm-rs-cc. This is necessary ## because ninja can handle only a single depfile per build target. ## .d files generated by llvm-rs-cc define .stamp, .bc, and optionally ## .java as build targets. However, there's no way to let ninja know ## dependencies to .bc files and .java files, so we give up build ## targets for them. As we write the .stamp file as the target by ## ourselves, the awk script removes the first lines before the colon ## and append a backslash to the last line to concatenate contents of ## multiple files. # $(1): .d files to be merged # $(2): merged .d file define _merge-renderscript-d $(hide) echo '$@: $(backslash)' > $2 $(foreach d,$1, \ $(hide) awk 'start { sub(/( \\)?$$/, " \\"); print } /:/ { start=1 }' < $d >> $2$(newline)) $(hide) echo >> $2 endef # b/37755219 RS_CC_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0:detect_container_overflow=0 define transform-renderscripts-to-java-and-bc @echo "RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)" $(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR) $(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/res/raw $(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/src $(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \ -o $(PRIVATE_RS_OUTPUT_DIR)/res/raw \ -p $(PRIVATE_RS_OUTPUT_DIR)/src \ -d $(PRIVATE_RS_OUTPUT_DIR) \ -a $@ -MD \ $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \ $(PRIVATE_RS_FLAGS) \ $(foreach inc,$(PRIVATE_RS_INCLUDES),$(addprefix -I , $(inc))) \ $(PRIVATE_RS_SOURCE_FILES) $(SOONG_ZIP) -o $@ -C $(PRIVATE_RS_OUTPUT_DIR)/src -D $(PRIVATE_RS_OUTPUT_DIR)/src $(SOONG_ZIP) -o $(PRIVATE_RS_OUTPUT_RES_ZIP) -C $(PRIVATE_RS_OUTPUT_DIR)/res -D $(PRIVATE_RS_OUTPUT_DIR)/res $(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d) endef define transform-bc-to-so @echo "Renderscript compatibility: $(notdir $@) <= $(notdir $<)" $(hide) mkdir -p $(dir $@) $(hide) $(BCC_COMPAT) -O3 -o $(dir $@)/$(notdir $(<:.bc=.o)) -fPIC -shared \ -rt-path $(RS_PREBUILT_CLCORE) -mtriple $(RS_COMPAT_TRIPLE) $< $(hide) $(PRIVATE_CXX_LINK) -fuse-ld=lld -target $(CLANG_TARGET_TRIPLE) -shared -Wl,-soname,$(notdir $@) -nostdlib \ -Wl,-rpath,\$$ORIGIN/../lib \ $(dir $@)/$(notdir $(<:.bc=.o)) \ $(RS_PREBUILT_COMPILER_RT) \ -o $@ $(CLANG_TARGET_GLOBAL_LLDFLAGS) -Wl,--hash-style=sysv \ -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib64 \ -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib \ $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so \ -lm -lc endef ########################################################### ## Commands to compile RenderScript to C++ ########################################################### define transform-renderscripts-to-cpp-and-bc @echo "RenderScript: $(PRIVATE_MODULE) <= $(PRIVATE_RS_SOURCE_FILES)" $(hide) rm -rf $(PRIVATE_RS_OUTPUT_DIR) $(hide) mkdir -p $(PRIVATE_RS_OUTPUT_DIR)/ $(hide) $(RS_CC_ASAN_OPTIONS) $(PRIVATE_RS_CC) \ -o $(PRIVATE_RS_OUTPUT_DIR)/ \ -d $(PRIVATE_RS_OUTPUT_DIR) \ -a $@ -MD \ -reflect-c++ \ $(addprefix -target-api , $(PRIVATE_RS_TARGET_API)) \ $(PRIVATE_RS_FLAGS) \ $(addprefix -I , $(PRIVATE_RS_INCLUDES)) \ $(PRIVATE_RS_SOURCE_FILES) $(call _merge-renderscript-d,$(PRIVATE_DEP_FILES),$@.d) $(hide) mkdir -p $(dir $@) $(hide) touch $@ endef ########################################################### ## Commands for running aidl ########################################################### define transform-aidl-to-java @mkdir -p $(dir $@) @echo "Aidl: $(PRIVATE_MODULE) <= $<" $(hide) $(AIDL) -d$(patsubst %.java,%.P,$@) $(PRIVATE_AIDL_FLAGS) $< $@ endef #$(AIDL) $(PRIVATE_AIDL_FLAGS) $< - | indent -nut -br -npcs -l1000 > $@ define transform-aidl-to-cpp @mkdir -p $(dir $@) @mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR) @echo "Generating C++ from AIDL: $(PRIVATE_MODULE) <= $<" $(hide) $(AIDL_CPP) -d$(basename $@).aidl.d --ninja $(PRIVATE_AIDL_FLAGS) \ $< $(PRIVATE_HEADER_OUTPUT_DIR) $@ endef ## Given a .aidl file path, generate the rule to compile it a .java file # $(1): a .aidl source file # $(2): a directory to place the generated .java files in # $(3): name of a variable to add the path to the generated source file to # # You must call this with $(eval). define define-aidl-java-rule define_aidl_java_rule_src := $(patsubst %.aidl,%.java,$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) $$(define_aidl_java_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL) $$(transform-aidl-to-java) $(3) += $$(define_aidl_java_rule_src) endef ## Given a .aidl file path generate the rule to compile it a .cpp file. # $(1): a .aidl source file # $(2): a directory to place the generated .cpp files in # $(3): name of a variable to add the path to the generated source file to # # You must call this with $(eval). define define-aidl-cpp-rule define_aidl_cpp_rule_src := $(patsubst %.aidl,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) $$(define_aidl_cpp_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL_CPP) $$(transform-aidl-to-cpp) $(3) += $$(define_aidl_cpp_rule_src) endef ########################################################### ## Commands for running vts ########################################################### define transform-vts-to-cpp @mkdir -p $(dir $@) @mkdir -p $(PRIVATE_HEADER_OUTPUT_DIR) @echo "Generating C++ from VTS: $(PRIVATE_MODULE) <= $<" $(hide) $(VTSC) -TODO_b/120496070 $(PRIVATE_VTS_FLAGS) \ $< $(PRIVATE_HEADER_OUTPUT_DIR) $@ endef ## Given a .vts file path generate the rule to compile it a .cpp file. # $(1): a .vts source file # $(2): a directory to place the generated .cpp files in # $(3): name of a variable to add the path to the generated source file to # # You must call this with $(eval). define define-vts-cpp-rule define_vts_cpp_rule_src := $(patsubst %.vts,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1)))) $$(define_vts_cpp_rule_src) : $(LOCAL_PATH)/$(1) $(VTSC) $$(transform-vts-to-cpp) $(3) += $$(define_vts_cpp_rule_src) endef ########################################################### ## Commands for running java-event-log-tags.py ########################################################### define transform-logtags-to-java @mkdir -p $(dir $@) @echo "logtags: $@ <= $<" $(hide) $(JAVATAGS) -o $@ $< endef ########################################################### ## Commands for running protoc to compile .proto into .java ########################################################### define transform-proto-to-java @mkdir -p $(dir $@) @echo "Protoc: $@ <= $(PRIVATE_PROTO_SRC_FILES)" @rm -rf $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) @mkdir -p $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) $(hide) for f in $(PRIVATE_PROTO_SRC_FILES); do \ $(PROTOC) \ $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \ $(PRIVATE_PROTO_JAVA_OUTPUT_OPTION)="$(PRIVATE_PROTO_JAVA_OUTPUT_PARAMS):$(PRIVATE_PROTO_JAVA_OUTPUT_DIR)" \ $(PRIVATE_PROTOC_FLAGS) \ $$f || exit 33; \ done $(SOONG_ZIP) -o $@ -C $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) -D $(PRIVATE_PROTO_JAVA_OUTPUT_DIR) endef ###################################################################### ## Commands for running protoc to compile .proto into .pb.cc (or.pb.c) and .pb.h ###################################################################### define transform-proto-to-cc @echo "Protoc: $@ <= $<" @mkdir -p $(dir $@) $(hide) \ $(PROTOC) \ $(addprefix --proto_path=, $(PRIVATE_PROTO_INCLUDES)) \ $(PRIVATE_PROTOC_FLAGS) \ $< @# aprotoc outputs only .cc. Rename it to .cpp if necessary. $(if $(PRIVATE_RENAME_CPP_EXT),\ $(hide) mv $(basename $@).cc $@) endef ########################################################### ## Helper to set include paths form transform-*-to-o ########################################################### define c-includes $(addprefix -I , $(PRIVATE_C_INCLUDES)) \ $(foreach i,$(PRIVATE_IMPORTED_INCLUDES),$(EXPORTS.$(i)))\ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\ $(addprefix -I ,\ $(filter-out $(PRIVATE_C_INCLUDES), \ $(PRIVATE_GLOBAL_C_INCLUDES))) \ $(addprefix -isystem ,\ $(filter-out $(PRIVATE_C_INCLUDES), \ $(PRIVATE_GLOBAL_C_SYSTEM_INCLUDES)))) endef ########################################################### ## Commands for running gcc to compile a C++ file ########################################################### define transform-cpp-to-o-compiler-args $(c-includes) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ $(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \ $(PRIVATE_ARM_CFLAGS) \ ) \ $(PRIVATE_RTTI_FLAG) \ $(PRIVATE_CFLAGS) \ $(PRIVATE_CPPFLAGS) \ $(PRIVATE_DEBUG_CFLAGS) \ $(PRIVATE_CFLAGS_NO_OVERRIDE) \ $(PRIVATE_CPPFLAGS_NO_OVERRIDE) endef # PATH_TO_CLANG_TIDY is defined in build/soong define call-clang-tidy $(PATH_TO_CLANG_TIDY) \ $(PRIVATE_TIDY_FLAGS) \ -checks=$(PRIVATE_TIDY_CHECKS) endef define clang-tidy-cpp $(hide) $(call-clang-tidy) $< -- $(transform-cpp-to-o-compiler-args) endef ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) define transform-cpp-to-o $(if $(PRIVATE_TIDY_CHECKS), @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C++: $<" $(clang-tidy-cpp)) endef else define transform-cpp-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-cpp)) $(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \ $(transform-cpp-to-o-compiler-args) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef endif ########################################################### ## Commands for running gcc to compile a C file ########################################################### # $(1): extra flags define transform-c-or-s-to-o-compiler-args $(c-includes) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ $(PRIVATE_TARGET_GLOBAL_CONLYFLAGS) \ $(PRIVATE_ARM_CFLAGS) \ ) \ $(1) endef define transform-c-to-o-compiler-args $(call transform-c-or-s-to-o-compiler-args, \ $(PRIVATE_CFLAGS) \ $(PRIVATE_CONLYFLAGS) \ $(PRIVATE_DEBUG_CFLAGS) \ $(PRIVATE_CFLAGS_NO_OVERRIDE)) endef define clang-tidy-c $(hide) $(call-clang-tidy) $< -- $(transform-c-to-o-compiler-args) endef ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) define transform-c-to-o $(if $(PRIVATE_TIDY_CHECKS), @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C: $<" $(clang-tidy-c)) endef else define transform-c-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-c)) $(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ $(transform-c-to-o-compiler-args) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef endif define transform-s-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(RELATIVE_PWD) $(PRIVATE_CC) \ $(call transform-c-or-s-to-o-compiler-args, $(PRIVATE_ASFLAGS)) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef # YASM compilation define transform-asm-to-o @mkdir -p $(dir $@) $(hide) $(YASM) \ $(addprefix -I , $(PRIVATE_C_INCLUDES)) \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_YASM_FLAGS) \ $(PRIVATE_ASFLAGS) \ -o $@ $< endef ########################################################### ## Commands for running gcc to compile an Objective-C file ## This should never happen for target builds but this ## will error at build time. ########################################################### define transform-m-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<" $(call transform-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS)) endef ########################################################### ## Commands for running gcc to compile a host C++ file ########################################################### define transform-host-cpp-to-o-compiler-args $(c-includes) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_CFLAGS) \ $(PRIVATE_HOST_GLOBAL_CPPFLAGS) \ ) \ $(PRIVATE_CFLAGS) \ $(PRIVATE_CPPFLAGS) \ $(PRIVATE_DEBUG_CFLAGS) \ $(PRIVATE_CFLAGS_NO_OVERRIDE) \ $(PRIVATE_CPPFLAGS_NO_OVERRIDE) endef define clang-tidy-host-cpp $(hide) $(call-clang-tidy) $< -- $(transform-host-cpp-to-o-compiler-args) endef ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) define transform-host-cpp-to-o $(if $(PRIVATE_TIDY_CHECKS), @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C++: $<" $(clang-tidy-host-cpp)) endef else define transform-host-cpp-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) C++: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-host-cpp)) $(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \ $(transform-host-cpp-to-o-compiler-args) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef endif ########################################################### ## Commands for running gcc to compile a host C file ########################################################### define transform-host-c-or-s-to-o-common-args $(c-includes) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_CFLAGS) \ $(PRIVATE_HOST_GLOBAL_CONLYFLAGS) \ ) endef # $(1): extra flags define transform-host-c-or-s-to-o @mkdir -p $(dir $@) $(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ $(transform-host-c-or-s-to-o-common-args) \ $(1) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef define transform-host-c-to-o-compiler-args $(transform-host-c-or-s-to-o-common-args) \ $(PRIVATE_CFLAGS) $(PRIVATE_CONLYFLAGS) \ $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE) endef define clang-tidy-host-c $(hide) $(call-clang-tidy) $< -- $(transform-host-c-to-o-compiler-args) endef ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY))) define transform-host-c-to-o $(if $(PRIVATE_TIDY_CHECKS), @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C: $<" $(clang-tidy-host-c)) endef else define transform-host-c-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) C: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(if $(PRIVATE_TIDY_CHECKS), $(clang-tidy-host-c)) $(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \ $(transform-host-c-to-o-compiler-args) \ -MD -MF $(patsubst %.o,%.d,$@) -o $@ $< endef endif define transform-host-s-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<" $(call transform-host-c-or-s-to-o, $(PRIVATE_ASFLAGS)) endef ########################################################### ## Commands for running gcc to compile a host Objective-C file ########################################################### define transform-host-m-to-o @echo "$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<" $(call transform-host-c-or-s-to-o, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE)) endef ########################################################### ## Commands for running gcc to compile a host Objective-C++ file ########################################################### define transform-host-mm-to-o $(transform-host-cpp-to-o) endef ########################################################### ## Rules to compile a single C/C++ source with ../ in the path ########################################################### # Replace "../" in object paths with $(DOTDOT_REPLACEMENT). DOTDOT_REPLACEMENT := dotdot/ ## Rule to compile a C++ source file with ../ in the path. ## Must be called with $(eval). # $(1): the C++ source file in LOCAL_SRC_FILES. # $(2): the additional dependencies. # $(3): the variable name to collect the output object file. # $(4): the ninja pool to use for the rule define compile-dotdot-cpp-file o := $(intermediates)/$(patsubst %$(LOCAL_CPP_EXTENSION),%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) $$(o) : .KATI_NINJA_POOL := $(4) $$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG_CXX) $$(transform-$$(PRIVATE_HOST)cpp-to-o) $$(call include-depfiles-for-objs, $$(o)) $(3) += $$(o) endef ## Rule to compile a C source file with ../ in the path. ## Must be called with $(eval). # $(1): the C source file in LOCAL_SRC_FILES. # $(2): the additional dependencies. # $(3): the variable name to collect the output object file. # $(4): the ninja pool to use for the rule define compile-dotdot-c-file o := $(intermediates)/$(patsubst %.c,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) $$(o) : .KATI_NINJA_POOL := $(4) $$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) $$(transform-$$(PRIVATE_HOST)c-to-o) $$(call include-depfiles-for-objs, $$(o)) $(3) += $$(o) endef ## Rule to compile a .S source file with ../ in the path. ## Must be called with $(eval). # $(1): the .S source file in LOCAL_SRC_FILES. # $(2): the additional dependencies. # $(3): the variable name to collect the output object file. # $(4): the ninja pool to use for the rule define compile-dotdot-s-file o := $(intermediates)/$(patsubst %.S,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) $$(o) : .KATI_NINJA_POOL := $(4) $$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) $$(transform-$$(PRIVATE_HOST)s-to-o) $$(call include-depfiles-for-objs, $$(o)) $(3) += $$(o) endef ## Rule to compile a .s source file with ../ in the path. ## Must be called with $(eval). # $(1): the .s source file in LOCAL_SRC_FILES. # $(2): the additional dependencies. # $(3): the variable name to collect the output object file. # $(4): the ninja pool to use for the rule define compile-dotdot-s-file-no-deps o := $(intermediates)/$(patsubst %.s,%.o,$(subst ../,$(DOTDOT_REPLACEMENT),$(1))) $$(o) : .KATI_NINJA_POOL := $(4) $$(o) : $(TOPDIR)$(LOCAL_PATH)/$(1) $(2) $(CLANG) $$(transform-$$(PRIVATE_HOST)s-to-o) $(3) += $$(o) endef ########################################################### ## Commands for running ar ########################################################### define _concat-if-arg2-not-empty $(if $(2),$(hide) $(1) $(2)) endef # Split long argument list into smaller groups and call the command repeatedly # Call the command at least once even if there are no arguments, as otherwise # the output file won't be created. # # $(1): the command without arguments # $(2): the arguments define split-long-arguments $(hide) $(1) $(wordlist 1,500,$(2)) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 501,1000,$(2))) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 1001,1500,$(2))) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 1501,2000,$(2))) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 2001,2500,$(2))) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 2501,3000,$(2))) $(call _concat-if-arg2-not-empty,$(1),$(wordlist 3001,99999,$(2))) endef # $(1): the full path of the source static library. # $(2): the full path of the destination static library. define _extract-and-include-single-target-whole-static-lib $(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\ rm -rf $$ldir; \ mkdir -p $$ldir; \ cp $(1) $$ldir; \ lib_to_include=$$ldir/$(notdir $(1)); \ filelist=; \ subdir=0; \ for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) t $(1)`; do \ if [ -e $$ldir/$$f ]; then \ mkdir $$ldir/$$subdir; \ ext=$$subdir/; \ subdir=$$((subdir+1)); \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) m $$lib_to_include $$f; \ else \ ext=; \ fi; \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \ filelist="$$filelist $$ldir/$$ext$$f"; \ done ; \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \ $(PRIVATE_ARFLAGS) $(2) $$filelist endef # $(1): the full path of the source static library. # $(2): the full path of the destination static library. define extract-and-include-whole-static-libs-first $(if $(strip $(1)), $(hide) cp $(1) $(2)) endef # $(1): the full path of the destination static library. define extract-and-include-target-whole-static-libs $(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1)) $(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \ $(call _extract-and-include-single-target-whole-static-lib, $(lib), $(1))) endef # Explicitly delete the archive first so that ar doesn't # try to add to an existing archive. define transform-o-to-static-lib @echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) @rm -f $@ $@.tmp $(call extract-and-include-target-whole-static-libs,$@.tmp) $(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_AR) \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_ARFLAGS) \ $(PRIVATE_ARFLAGS) \ $@.tmp,$(PRIVATE_ALL_OBJECTS)) $(hide) mv -f $@.tmp $@ endef ########################################################### ## Commands for running host ar ########################################################### # $(1): the full path of the source static library. # $(2): the full path of the destination static library. define _extract-and-include-single-host-whole-static-lib $(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\ rm -rf $$ldir; \ mkdir -p $$ldir; \ cp $(1) $$ldir; \ lib_to_include=$$ldir/$(notdir $(1)); \ filelist=; \ subdir=0; \ for f in `$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) t $(1) | \grep '\.o$$'`; do \ if [ -e $$ldir/$$f ]; then \ mkdir $$ldir/$$subdir; \ ext=$$subdir/; \ subdir=$$((subdir+1)); \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) m $$lib_to_include $$f; \ else \ ext=; \ fi; \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \ filelist="$$filelist $$ldir/$$ext$$f"; \ done ; \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) \ $(2) $$filelist endef define extract-and-include-host-whole-static-libs $(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)),$(1)) $(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \ $(call _extract-and-include-single-host-whole-static-lib, $(lib),$(1))) endef ifeq ($(HOST_OS),darwin) # On Darwin the host ar fails if there is nothing to add to .a at all. # We work around by adding a dummy.o and then deleting it. define create-dummy.o-if-no-objs $(if $(PRIVATE_ALL_OBJECTS),,$(hide) touch $(dir $(1))dummy.o) endef define get-dummy.o-if-no-objs $(if $(PRIVATE_ALL_OBJECTS),,$(dir $(1))dummy.o) endef define delete-dummy.o-if-no-objs $(if $(PRIVATE_ALL_OBJECTS),,$(hide) $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) d $(1) $(dir $(1))dummy.o \ && rm -f $(dir $(1))dummy.o) endef else create-dummy.o-if-no-objs = get-dummy.o-if-no-objs = delete-dummy.o-if-no-objs = endif # HOST_OS is darwin # Explicitly delete the archive first so that ar doesn't # try to add to an existing archive. define transform-host-o-to-static-lib @echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) @rm -f $@ $@.tmp $(call extract-and-include-host-whole-static-libs,$@.tmp) $(call create-dummy.o-if-no-objs,$@.tmp) $(call split-long-arguments,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)AR) \ $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)GLOBAL_ARFLAGS) $@.tmp,\ $(PRIVATE_ALL_OBJECTS) $(call get-dummy.o-if-no-objs,$@.tmp)) $(call delete-dummy.o-if-no-objs,$@.tmp) $(hide) mv -f $@.tmp $@ endef ########################################################### ## Commands for running gcc to link a shared library or package ########################################################### # ld just seems to be so finicky with command order that we allow # it to be overriden en-masse see combo/linux-arm.make for an example. ifneq ($(HOST_CUSTOM_LD_COMMAND),true) define transform-host-o-to-shared-lib-inner $(hide) $(PRIVATE_CXX_LINK) \ -Wl,-rpath,\$$ORIGIN/../$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \ -Wl,-rpath,\$$ORIGIN/$(notdir $($(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)OUT_SHARED_LIBRARIES)) \ -shared -Wl,-soname,$(notdir $@) \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ ) \ $(PRIVATE_LDFLAGS) \ $(PRIVATE_CRTBEGIN) \ $(PRIVATE_ALL_OBJECTS) \ -Wl,--whole-archive \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ -Wl,--no-whole-archive \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \ $(PRIVATE_LIBCRT_BUILTINS) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ -o $@ \ $(PRIVATE_CRTEND) \ $(PRIVATE_LDLIBS) endef endif define transform-host-o-to-shared-lib @echo "$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-host-o-to-shared-lib-inner) endef define transform-host-o-to-package @echo "$($(PRIVATE_PREFIX)DISPLAY) Package: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-host-o-to-shared-lib-inner) endef ########################################################### ## Commands for running gcc to link a shared library or package ########################################################### define transform-o-to-shared-lib-inner $(hide) $(PRIVATE_CXX_LINK) \ -nostdlib -Wl,-soname,$(notdir $@) \ -Wl,--gc-sections \ -shared \ $(PRIVATE_TARGET_CRTBEGIN_SO_O) \ $(PRIVATE_ALL_OBJECTS) \ -Wl,--whole-archive \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ -Wl,--no-whole-archive \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ $(PRIVATE_LDFLAGS) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ -o $@ \ $(PRIVATE_TARGET_CRTEND_SO_O) \ $(PRIVATE_LDLIBS) endef define transform-o-to-shared-lib @echo "$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-o-to-shared-lib-inner) endef ########################################################### ## Commands for running gcc to link an executable ########################################################### define transform-o-to-executable-inner $(hide) $(PRIVATE_CXX_LINK) -pie \ -nostdlib -Bdynamic \ -Wl,-dynamic-linker,$(PRIVATE_LINKER) \ -Wl,--gc-sections \ -Wl,-z,nocopyreloc \ $(PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O) \ $(PRIVATE_ALL_OBJECTS) \ -Wl,--whole-archive \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ -Wl,--no-whole-archive \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ $(PRIVATE_LDFLAGS) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ -o $@ \ $(PRIVATE_TARGET_CRTEND_O) \ $(PRIVATE_LDLIBS) endef define transform-o-to-executable @echo "$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-o-to-executable-inner) endef ########################################################### ## Commands for linking a static executable. In practice, ## we only use this on arm, so the other platforms don't ## have transform-o-to-static-executable defined. ## Clang driver needs -static to create static executable. ## However, bionic/linker uses -shared to overwrite. ## Linker for x86 targets does not allow coexistance of -static and -shared, ## so we add -static only if -shared is not used. ########################################################### define transform-o-to-static-executable-inner $(hide) $(PRIVATE_CXX_LINK) \ -nostdlib -Bstatic \ $(if $(filter $(PRIVATE_LDFLAGS),-shared),,-static) \ -Wl,--gc-sections \ -o $@ \ $(PRIVATE_TARGET_CRTBEGIN_STATIC_O) \ $(PRIVATE_TARGET_GLOBAL_LDFLAGS) \ $(PRIVATE_LDFLAGS) \ $(PRIVATE_ALL_OBJECTS) \ -Wl,--whole-archive \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ -Wl,--no-whole-archive \ $(filter-out %libcompiler_rt.hwasan.a %libc_nomalloc.hwasan.a %libc.hwasan.a %libcompiler_rt.a %libc_nomalloc.a %libc.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ -Wl,--start-group \ $(filter %libc.a %libc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ $(filter %libc_nomalloc.a %libc_nomalloc.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \ $(filter %libcompiler_rt.a %libcompiler_rt.hwasan.a,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ $(PRIVATE_TARGET_LIBCRT_BUILTINS) \ -Wl,--end-group \ $(PRIVATE_TARGET_CRTEND_O) endef define transform-o-to-static-executable @echo "$($(PRIVATE_PREFIX)DISPLAY) StaticExecutable: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-o-to-static-executable-inner) endef ########################################################### ## Commands for running gcc to link a host executable ########################################################### ifneq ($(HOST_CUSTOM_LD_COMMAND),true) define transform-host-o-to-executable-inner $(hide) $(PRIVATE_CXX_LINK) \ $(PRIVATE_CRTBEGIN) \ $(PRIVATE_ALL_OBJECTS) \ -Wl,--whole-archive \ $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES) \ -Wl,--no-whole-archive \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \ $(PRIVATE_ALL_STATIC_LIBRARIES) \ $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \ $(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_HOST_LIBPROFILE_RT)) \ $(PRIVATE_LIBCRT_BUILTINS) \ $(PRIVATE_ALL_SHARED_LIBRARIES) \ $(foreach path,$(PRIVATE_RPATHS), \ -Wl,-rpath,\$$ORIGIN/$(path)) \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_HOST_GLOBAL_LDFLAGS) \ ) \ $(PRIVATE_LDFLAGS) \ -o $@ \ $(PRIVATE_CRTEND) \ $(PRIVATE_LDLIBS) endef endif define transform-host-o-to-executable @echo "$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(transform-host-o-to-executable-inner) endef ########################################################### ## Commands for packaging native coverage files ########################################################### define package-coverage-files @rm -f $@ $@.lst $@.premerged @touch $@.lst $(foreach obj,$(strip $(PRIVATE_ALL_OBJECTS)), $(hide) echo $(obj) >> $@.lst$(newline)) $(hide) $(SOONG_ZIP) -o $@.premerged -C $(OUT_DIR) -l $@.lst $(hide) $(MERGE_ZIPS) -ignore-duplicates $@ $@.premerged $(strip $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) endef ########################################################### ## Commands for running javac to make .class files ########################################################### # b/37750224 AAPT_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0 # Search for generated R.java in $1, copy the found R.java as $2. define find-generated-R.java $(hide) for GENERATED_R_FILE in `find $(1) \ -name R.java 2> /dev/null`; do \ cp $$GENERATED_R_FILE $(2) || exit 32; \ done; @# Ensure that the target file is always created, i.e. also in case we did not @# enter the GENERATED_R_FILE-loop above. This avoids unnecessary rebuilding. $(hide) touch $(2) endef ########################################################### # AAPT2 compilation and link ########################################################### define aapt2-compile-one-resource-file @mkdir -p $(dir $@) $(hide) $(AAPT2) compile -o $(dir $@) $(PRIVATE_AAPT2_CFLAGS) $< endef define aapt2-compile-resource-dirs @mkdir -p $(dir $@) $(hide) $(AAPT2) compile -o $@ $(addprefix --dir ,$(PRIVATE_SOURCE_RES_DIRS)) \ $(PRIVATE_AAPT2_CFLAGS) endef # TODO(b/74574557): use aapt2 compile --zip if it gets implemented define aapt2-compile-resource-zips @mkdir -p $(dir $@) $(ZIPSYNC) -d $@.contents -l $@.list $(PRIVATE_SOURCE_RES_ZIPS) $(hide) $(AAPT2) compile -o $@ --dir $@.contents $(PRIVATE_AAPT2_CFLAGS) endef # Set up rule to compile one resource file with aapt2. # Must be called with $(eval). # $(1): the source file # $(2): the output file define aapt2-compile-one-resource-file-rule $(2) : $(1) $(AAPT2) @echo "AAPT2 compile $$@ <- $$<" $$(call aapt2-compile-one-resource-file) endef # Convert input resource file path to output file path. # values-[config]/.xml -> values-[config]_.arsc.flat; # For other resource file, just replace the last "/" with "_" and # add .flat extension. # # $(1): the input resource file path # $(2): the base dir of the output file path # Returns: the compiled output file path define aapt2-compiled-resource-out-file $(strip \ $(eval _p_w := $(strip $(subst /,$(space),$(dir $(call clean-path,$(1)))))) $(2)/$(subst $(space),/,$(_p_w))_$(if $(filter values%,$(lastword $(_p_w))),$(patsubst %.xml,%.arsc,$(notdir $(1))),$(notdir $(1))).flat) endef define aapt2-link @mkdir -p $(dir $@) rm -rf $(PRIVATE_JAVA_GEN_DIR) mkdir -p $(PRIVATE_JAVA_GEN_DIR) $(call dump-words-to-file,$(PRIVATE_RES_FLAT),$(dir $@)aapt2-flat-list) $(call dump-words-to-file,$(PRIVATE_OVERLAY_FLAT),$(dir $@)aapt2-flat-overlay-list) cat $(PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS) | sort -u | tr '\n' ' ' > $(dir $@)aapt2-transitive-overlay-list $(hide) $(AAPT2) link -o $@ \ $(PRIVATE_AAPT_FLAGS) \ $(if $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES),$$(cat $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES))) \ $(addprefix --manifest ,$(PRIVATE_ANDROID_MANIFEST)) \ $(addprefix -I ,$(PRIVATE_AAPT_INCLUDES)) \ $(addprefix -I ,$(PRIVATE_SHARED_ANDROID_LIBRARIES)) \ $(addprefix -A ,$(foreach d,$(PRIVATE_ASSET_DIR),$(call clean-path,$(d)))) \ $(addprefix --java ,$(PRIVATE_JAVA_GEN_DIR)) \ $(addprefix --proguard ,$(PRIVATE_PROGUARD_OPTIONS_FILE)) \ $(addprefix --min-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ $(addprefix --target-sdk-version ,$(PRIVATE_DEFAULT_APP_TARGET_SDK)) \ $(if $(filter --product,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --product ,$(PRIVATE_TARGET_AAPT_CHARACTERISTICS))) \ $(addprefix -c ,$(PRIVATE_PRODUCT_AAPT_CONFIG)) \ $(addprefix --preferred-density ,$(PRIVATE_PRODUCT_AAPT_PREF_CONFIG)) \ $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \ $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \ $(addprefix --rename-manifest-package ,$(PRIVATE_MANIFEST_PACKAGE_NAME)) \ $(addprefix --rename-instrumentation-target-package ,$(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \ -R \@$(dir $@)aapt2-flat-overlay-list \ -R \@$(dir $@)aapt2-transitive-overlay-list \ \@$(dir $@)aapt2-flat-list $(SOONG_ZIP) -o $(PRIVATE_SRCJAR) -C $(PRIVATE_JAVA_GEN_DIR) -D $(PRIVATE_JAVA_GEN_DIR) $(EXTRACT_JAR_PACKAGES) -i $(PRIVATE_SRCJAR) -o $(PRIVATE_AAPT_EXTRA_PACKAGES) --prefix '--extra-packages ' endef define _create-default-manifest-file $(1): rm -f $1 (echo '' && \ echo ' ' && \ echo '' ) > $1 endef define create-default-manifest-file $(eval $(call _create-default-manifest-file,$(1),$(2))) endef ########################################################### xlint_unchecked := -Xlint:unchecked # emit-line, , define emit-line $(if $(1),echo -n '$(strip $(1)) ' >> $(2)) endef # dump-words-to-file, , define dump-words-to-file @rm -f $(2) @touch $(2) @$(call emit-line,$(wordlist 1,500,$(1)),$(2)) @$(call emit-line,$(wordlist 501,1000,$(1)),$(2)) @$(call emit-line,$(wordlist 1001,1500,$(1)),$(2)) @$(call emit-line,$(wordlist 1501,2000,$(1)),$(2)) @$(call emit-line,$(wordlist 2001,2500,$(1)),$(2)) @$(call emit-line,$(wordlist 2501,3000,$(1)),$(2)) @$(call emit-line,$(wordlist 3001,3500,$(1)),$(2)) @$(call emit-line,$(wordlist 3501,4000,$(1)),$(2)) @$(call emit-line,$(wordlist 4001,4500,$(1)),$(2)) @$(call emit-line,$(wordlist 4501,5000,$(1)),$(2)) @$(call emit-line,$(wordlist 5001,5500,$(1)),$(2)) @$(call emit-line,$(wordlist 5501,6000,$(1)),$(2)) @$(call emit-line,$(wordlist 6001,6500,$(1)),$(2)) @$(call emit-line,$(wordlist 6501,7000,$(1)),$(2)) @$(call emit-line,$(wordlist 7001,7500,$(1)),$(2)) @$(call emit-line,$(wordlist 7501,8000,$(1)),$(2)) @$(call emit-line,$(wordlist 8001,8500,$(1)),$(2)) @$(call emit-line,$(wordlist 8501,9000,$(1)),$(2)) @$(call emit-line,$(wordlist 9001,9500,$(1)),$(2)) @$(call emit-line,$(wordlist 9501,10000,$(1)),$(2)) @$(call emit-line,$(wordlist 10001,10500,$(1)),$(2)) @$(call emit-line,$(wordlist 10501,11000,$(1)),$(2)) @$(call emit-line,$(wordlist 11001,11500,$(1)),$(2)) @$(call emit-line,$(wordlist 11501,12000,$(1)),$(2)) @$(call emit-line,$(wordlist 12001,12500,$(1)),$(2)) @$(call emit-line,$(wordlist 12501,13000,$(1)),$(2)) @$(call emit-line,$(wordlist 13001,13500,$(1)),$(2)) @$(call emit-line,$(wordlist 13501,14000,$(1)),$(2)) @$(call emit-line,$(wordlist 14001,14500,$(1)),$(2)) @$(call emit-line,$(wordlist 14501,15000,$(1)),$(2)) @$(call emit-line,$(wordlist 15001,15500,$(1)),$(2)) @$(call emit-line,$(wordlist 15501,16000,$(1)),$(2)) @$(call emit-line,$(wordlist 16001,16500,$(1)),$(2)) @$(call emit-line,$(wordlist 16501,17000,$(1)),$(2)) @$(call emit-line,$(wordlist 17001,17500,$(1)),$(2)) @$(call emit-line,$(wordlist 17501,18000,$(1)),$(2)) @$(call emit-line,$(wordlist 18001,18500,$(1)),$(2)) @$(call emit-line,$(wordlist 18501,19000,$(1)),$(2)) @$(call emit-line,$(wordlist 19001,19500,$(1)),$(2)) @$(call emit-line,$(wordlist 19501,20000,$(1)),$(2)) @$(call emit-line,$(wordlist 20001,20500,$(1)),$(2)) @$(call emit-line,$(wordlist 20501,21000,$(1)),$(2)) @$(call emit-line,$(wordlist 21001,21500,$(1)),$(2)) @$(call emit-line,$(wordlist 21501,22000,$(1)),$(2)) @$(call emit-line,$(wordlist 22001,22500,$(1)),$(2)) @$(call emit-line,$(wordlist 22501,23000,$(1)),$(2)) @$(call emit-line,$(wordlist 23001,23500,$(1)),$(2)) @$(call emit-line,$(wordlist 23501,24000,$(1)),$(2)) @$(call emit-line,$(wordlist 24001,24500,$(1)),$(2)) @$(call emit-line,$(wordlist 24501,25000,$(1)),$(2)) @$(call emit-line,$(wordlist 25001,25500,$(1)),$(2)) @$(call emit-line,$(wordlist 25501,26000,$(1)),$(2)) @$(call emit-line,$(wordlist 26001,26500,$(1)),$(2)) @$(call emit-line,$(wordlist 26501,27000,$(1)),$(2)) @$(call emit-line,$(wordlist 27001,27500,$(1)),$(2)) @$(call emit-line,$(wordlist 27501,28000,$(1)),$(2)) @$(call emit-line,$(wordlist 28001,28500,$(1)),$(2)) @$(call emit-line,$(wordlist 28501,29000,$(1)),$(2)) @$(call emit-line,$(wordlist 29001,29500,$(1)),$(2)) @$(call emit-line,$(wordlist 29501,30000,$(1)),$(2)) @$(call emit-line,$(wordlist 30001,30500,$(1)),$(2)) @$(call emit-line,$(wordlist 30501,31000,$(1)),$(2)) @$(call emit-line,$(wordlist 31001,31500,$(1)),$(2)) @$(call emit-line,$(wordlist 31501,32000,$(1)),$(2)) @$(call emit-line,$(wordlist 32001,32500,$(1)),$(2)) @$(call emit-line,$(wordlist 32501,33000,$(1)),$(2)) @$(call emit-line,$(wordlist 33001,33500,$(1)),$(2)) @$(call emit-line,$(wordlist 33501,34000,$(1)),$(2)) @$(call emit-line,$(wordlist 34001,34500,$(1)),$(2)) @$(call emit-line,$(wordlist 34501,35000,$(1)),$(2)) @$(call emit-line,$(wordlist 35001,35500,$(1)),$(2)) @$(call emit-line,$(wordlist 35501,36000,$(1)),$(2)) @$(call emit-line,$(wordlist 36001,36500,$(1)),$(2)) @$(call emit-line,$(wordlist 36501,37000,$(1)),$(2)) @$(call emit-line,$(wordlist 37001,37500,$(1)),$(2)) @$(call emit-line,$(wordlist 37501,38000,$(1)),$(2)) @$(call emit-line,$(wordlist 38001,38500,$(1)),$(2)) @$(call emit-line,$(wordlist 38501,39000,$(1)),$(2)) @$(call emit-line,$(wordlist 39001,39500,$(1)),$(2)) @$(call emit-line,$(wordlist 39501,40000,$(1)),$(2)) @$(call emit-line,$(wordlist 40001,40500,$(1)),$(2)) @$(call emit-line,$(wordlist 40501,41000,$(1)),$(2)) @$(call emit-line,$(wordlist 41001,41500,$(1)),$(2)) @$(call emit-line,$(wordlist 41501,42000,$(1)),$(2)) @$(call emit-line,$(wordlist 42001,42500,$(1)),$(2)) @$(call emit-line,$(wordlist 42501,43000,$(1)),$(2)) @$(call emit-line,$(wordlist 43001,43500,$(1)),$(2)) @$(call emit-line,$(wordlist 43501,44000,$(1)),$(2)) @$(call emit-line,$(wordlist 44001,44500,$(1)),$(2)) @$(call emit-line,$(wordlist 44501,45000,$(1)),$(2)) @$(call emit-line,$(wordlist 45001,45500,$(1)),$(2)) @$(call emit-line,$(wordlist 45501,46000,$(1)),$(2)) @$(call emit-line,$(wordlist 46001,46500,$(1)),$(2)) @$(call emit-line,$(wordlist 46501,47000,$(1)),$(2)) @$(call emit-line,$(wordlist 47001,47500,$(1)),$(2)) @$(call emit-line,$(wordlist 47501,48000,$(1)),$(2)) @$(call emit-line,$(wordlist 48001,48500,$(1)),$(2)) @$(call emit-line,$(wordlist 48501,49000,$(1)),$(2)) @$(call emit-line,$(wordlist 49001,49500,$(1)),$(2)) @$(call emit-line,$(wordlist 49501,50000,$(1)),$(2)) @$(call emit-line,$(wordlist 50001,50500,$(1)),$(2)) @$(call emit-line,$(wordlist 50501,51000,$(1)),$(2)) @$(call emit-line,$(wordlist 51001,51500,$(1)),$(2)) @$(call emit-line,$(wordlist 51501,52000,$(1)),$(2)) @$(call emit-line,$(wordlist 52001,52500,$(1)),$(2)) @$(call emit-line,$(wordlist 52501,53000,$(1)),$(2)) @$(call emit-line,$(wordlist 53001,53500,$(1)),$(2)) @$(call emit-line,$(wordlist 53501,54000,$(1)),$(2)) @$(call emit-line,$(wordlist 54001,54500,$(1)),$(2)) @$(call emit-line,$(wordlist 54501,55000,$(1)),$(2)) @$(call emit-line,$(wordlist 55001,55500,$(1)),$(2)) @$(call emit-line,$(wordlist 55501,56000,$(1)),$(2)) @$(call emit-line,$(wordlist 56001,56500,$(1)),$(2)) @$(call emit-line,$(wordlist 56501,57000,$(1)),$(2)) @$(call emit-line,$(wordlist 57001,57500,$(1)),$(2)) @$(call emit-line,$(wordlist 57501,58000,$(1)),$(2)) @$(call emit-line,$(wordlist 58001,58500,$(1)),$(2)) @$(call emit-line,$(wordlist 58501,59000,$(1)),$(2)) @$(call emit-line,$(wordlist 59001,59500,$(1)),$(2)) @$(call emit-line,$(wordlist 59501,60000,$(1)),$(2)) @$(call emit-line,$(wordlist 60001,60500,$(1)),$(2)) @$(call emit-line,$(wordlist 60501,61000,$(1)),$(2)) @$(call emit-line,$(wordlist 61001,61500,$(1)),$(2)) @$(call emit-line,$(wordlist 61501,62000,$(1)),$(2)) @$(call emit-line,$(wordlist 62001,62500,$(1)),$(2)) @$(call emit-line,$(wordlist 62501,63000,$(1)),$(2)) @$(call emit-line,$(wordlist 63001,63500,$(1)),$(2)) @$(call emit-line,$(wordlist 63501,64000,$(1)),$(2)) @$(call emit-line,$(wordlist 64001,64500,$(1)),$(2)) @$(call emit-line,$(wordlist 64501,65000,$(1)),$(2)) @$(call emit-line,$(wordlist 65001,65500,$(1)),$(2)) @$(call emit-line,$(wordlist 65501,66000,$(1)),$(2)) @$(call emit-line,$(wordlist 66001,66500,$(1)),$(2)) @$(call emit-line,$(wordlist 66501,67000,$(1)),$(2)) @$(call emit-line,$(wordlist 67001,67500,$(1)),$(2)) @$(call emit-line,$(wordlist 67501,68000,$(1)),$(2)) @$(call emit-line,$(wordlist 68001,68500,$(1)),$(2)) @$(call emit-line,$(wordlist 68501,69000,$(1)),$(2)) @$(call emit-line,$(wordlist 69001,69500,$(1)),$(2)) @$(call emit-line,$(wordlist 69501,70000,$(1)),$(2)) @$(call emit-line,$(wordlist 70001,70500,$(1)),$(2)) @$(call emit-line,$(wordlist 70501,71000,$(1)),$(2)) @$(call emit-line,$(wordlist 71001,71500,$(1)),$(2)) @$(call emit-line,$(wordlist 71501,72000,$(1)),$(2)) @$(call emit-line,$(wordlist 72001,72500,$(1)),$(2)) @$(call emit-line,$(wordlist 72501,73000,$(1)),$(2)) @$(call emit-line,$(wordlist 73001,73500,$(1)),$(2)) @$(call emit-line,$(wordlist 73501,74000,$(1)),$(2)) @$(call emit-line,$(wordlist 74001,74500,$(1)),$(2)) @$(call emit-line,$(wordlist 74501,75000,$(1)),$(2)) @$(call emit-line,$(wordlist 75001,75500,$(1)),$(2)) @$(call emit-line,$(wordlist 75501,76000,$(1)),$(2)) @$(call emit-line,$(wordlist 76001,76500,$(1)),$(2)) @$(call emit-line,$(wordlist 76501,77000,$(1)),$(2)) @$(call emit-line,$(wordlist 77001,77500,$(1)),$(2)) @$(call emit-line,$(wordlist 77501,78000,$(1)),$(2)) @$(call emit-line,$(wordlist 78001,78500,$(1)),$(2)) @$(call emit-line,$(wordlist 78501,79000,$(1)),$(2)) @$(call emit-line,$(wordlist 79001,79500,$(1)),$(2)) @$(call emit-line,$(wordlist 79501,80000,$(1)),$(2)) @$(call emit-line,$(wordlist 80001,80500,$(1)),$(2)) @$(call emit-line,$(wordlist 80501,81000,$(1)),$(2)) @$(call emit-line,$(wordlist 81001,81500,$(1)),$(2)) @$(call emit-line,$(wordlist 81501,82000,$(1)),$(2)) @$(call emit-line,$(wordlist 82001,82500,$(1)),$(2)) @$(call emit-line,$(wordlist 82501,83000,$(1)),$(2)) @$(call emit-line,$(wordlist 83001,83500,$(1)),$(2)) @$(call emit-line,$(wordlist 83501,84000,$(1)),$(2)) @$(call emit-line,$(wordlist 84001,84500,$(1)),$(2)) @$(call emit-line,$(wordlist 84501,85000,$(1)),$(2)) @$(call emit-line,$(wordlist 85001,85500,$(1)),$(2)) @$(call emit-line,$(wordlist 85501,86000,$(1)),$(2)) @$(call emit-line,$(wordlist 86001,86500,$(1)),$(2)) @$(call emit-line,$(wordlist 86501,87000,$(1)),$(2)) @$(call emit-line,$(wordlist 87001,87500,$(1)),$(2)) @$(call emit-line,$(wordlist 87501,88000,$(1)),$(2)) @$(call emit-line,$(wordlist 88001,88500,$(1)),$(2)) @$(call emit-line,$(wordlist 88501,89000,$(1)),$(2)) @$(call emit-line,$(wordlist 89001,89500,$(1)),$(2)) @$(call emit-line,$(wordlist 89501,90000,$(1)),$(2)) @$(call emit-line,$(wordlist 90001,90500,$(1)),$(2)) @$(call emit-line,$(wordlist 90501,91000,$(1)),$(2)) @$(call emit-line,$(wordlist 91001,91500,$(1)),$(2)) @$(call emit-line,$(wordlist 91501,92000,$(1)),$(2)) @$(call emit-line,$(wordlist 92001,92500,$(1)),$(2)) @$(call emit-line,$(wordlist 92501,93000,$(1)),$(2)) @$(call emit-line,$(wordlist 93001,93500,$(1)),$(2)) @$(call emit-line,$(wordlist 93501,94000,$(1)),$(2)) @$(call emit-line,$(wordlist 94001,94500,$(1)),$(2)) @$(call emit-line,$(wordlist 94501,95000,$(1)),$(2)) @$(call emit-line,$(wordlist 95001,95500,$(1)),$(2)) @$(call emit-line,$(wordlist 95501,96000,$(1)),$(2)) @$(call emit-line,$(wordlist 96001,96500,$(1)),$(2)) @$(call emit-line,$(wordlist 96501,97000,$(1)),$(2)) @$(call emit-line,$(wordlist 97001,97500,$(1)),$(2)) @$(call emit-line,$(wordlist 97501,98000,$(1)),$(2)) @$(call emit-line,$(wordlist 98001,98500,$(1)),$(2)) @$(call emit-line,$(wordlist 98501,99000,$(1)),$(2)) @$(call emit-line,$(wordlist 99001,99500,$(1)),$(2)) @$(call emit-line,$(wordlist 99501,100000,$(1)),$(2)) @$(call emit-line,$(wordlist 100001,100500,$(1)),$(2)) @$(call emit-line,$(wordlist 100501,101000,$(1)),$(2)) @$(call emit-line,$(wordlist 101001,101500,$(1)),$(2)) @$(call emit-line,$(wordlist 101501,102000,$(1)),$(2)) @$(call emit-line,$(wordlist 102001,102500,$(1)),$(2)) @$(call emit-line,$(wordlist 102501,103000,$(1)),$(2)) @$(call emit-line,$(wordlist 103001,103500,$(1)),$(2)) @$(call emit-line,$(wordlist 103501,104000,$(1)),$(2)) @$(call emit-line,$(wordlist 104001,104500,$(1)),$(2)) @$(call emit-line,$(wordlist 104501,105000,$(1)),$(2)) @$(call emit-line,$(wordlist 105001,105500,$(1)),$(2)) @$(call emit-line,$(wordlist 105501,106000,$(1)),$(2)) @$(call emit-line,$(wordlist 106001,106500,$(1)),$(2)) @$(call emit-line,$(wordlist 106501,107000,$(1)),$(2)) @$(call emit-line,$(wordlist 107001,107500,$(1)),$(2)) @$(call emit-line,$(wordlist 107501,108000,$(1)),$(2)) @$(call emit-line,$(wordlist 108001,108500,$(1)),$(2)) @$(call emit-line,$(wordlist 108501,109000,$(1)),$(2)) @$(call emit-line,$(wordlist 109001,109500,$(1)),$(2)) @$(call emit-line,$(wordlist 109501,110000,$(1)),$(2)) @$(call emit-line,$(wordlist 110001,110500,$(1)),$(2)) @$(call emit-line,$(wordlist 110501,111000,$(1)),$(2)) @$(call emit-line,$(wordlist 111001,111500,$(1)),$(2)) @$(call emit-line,$(wordlist 111501,112000,$(1)),$(2)) @$(call emit-line,$(wordlist 112001,112500,$(1)),$(2)) @$(call emit-line,$(wordlist 112501,113000,$(1)),$(2)) @$(call emit-line,$(wordlist 113001,113500,$(1)),$(2)) @$(call emit-line,$(wordlist 113501,114000,$(1)),$(2)) @$(call emit-line,$(wordlist 114001,114500,$(1)),$(2)) @$(call emit-line,$(wordlist 114501,115000,$(1)),$(2)) @$(call emit-line,$(wordlist 115001,115500,$(1)),$(2)) @$(call emit-line,$(wordlist 115501,116000,$(1)),$(2)) @$(call emit-line,$(wordlist 116001,116500,$(1)),$(2)) @$(call emit-line,$(wordlist 116501,117000,$(1)),$(2)) @$(call emit-line,$(wordlist 117001,117500,$(1)),$(2)) @$(call emit-line,$(wordlist 117501,118000,$(1)),$(2)) @$(call emit-line,$(wordlist 118001,118500,$(1)),$(2)) @$(call emit-line,$(wordlist 118501,119000,$(1)),$(2)) @$(call emit-line,$(wordlist 119001,119500,$(1)),$(2)) @$(call emit-line,$(wordlist 119501,120000,$(1)),$(2)) @$(call emit-line,$(wordlist 120001,120500,$(1)),$(2)) @$(call emit-line,$(wordlist 120501,121000,$(1)),$(2)) @$(call emit-line,$(wordlist 121001,121500,$(1)),$(2)) @$(call emit-line,$(wordlist 121501,122000,$(1)),$(2)) @$(call emit-line,$(wordlist 122001,122500,$(1)),$(2)) @$(call emit-line,$(wordlist 122501,123000,$(1)),$(2)) @$(call emit-line,$(wordlist 123001,123500,$(1)),$(2)) @$(call emit-line,$(wordlist 123501,124000,$(1)),$(2)) @$(call emit-line,$(wordlist 124001,124500,$(1)),$(2)) @$(call emit-line,$(wordlist 124501,125000,$(1)),$(2)) @$(call emit-line,$(wordlist 125001,125500,$(1)),$(2)) @$(call emit-line,$(wordlist 125501,126000,$(1)),$(2)) @$(call emit-line,$(wordlist 126001,126500,$(1)),$(2)) @$(call emit-line,$(wordlist 126501,127000,$(1)),$(2)) @$(call emit-line,$(wordlist 127001,127500,$(1)),$(2)) @$(call emit-line,$(wordlist 127501,128000,$(1)),$(2)) @$(call emit-line,$(wordlist 128001,128500,$(1)),$(2)) @$(call emit-line,$(wordlist 128501,129000,$(1)),$(2)) @$(call emit-line,$(wordlist 129001,129500,$(1)),$(2)) @$(call emit-line,$(wordlist 129501,130000,$(1)),$(2)) @$(call emit-line,$(wordlist 130001,130500,$(1)),$(2)) @$(call emit-line,$(wordlist 130501,131000,$(1)),$(2)) @$(call emit-line,$(wordlist 131001,131500,$(1)),$(2)) @$(call emit-line,$(wordlist 131501,132000,$(1)),$(2)) @$(call emit-line,$(wordlist 132001,132500,$(1)),$(2)) @$(call emit-line,$(wordlist 132501,133000,$(1)),$(2)) @$(call emit-line,$(wordlist 133001,133500,$(1)),$(2)) @$(call emit-line,$(wordlist 133501,134000,$(1)),$(2)) @$(call emit-line,$(wordlist 134001,134500,$(1)),$(2)) @$(call emit-line,$(wordlist 134501,135000,$(1)),$(2)) @$(call emit-line,$(wordlist 135001,135500,$(1)),$(2)) @$(call emit-line,$(wordlist 135501,136000,$(1)),$(2)) @$(call emit-line,$(wordlist 136001,136500,$(1)),$(2)) @$(call emit-line,$(wordlist 136501,137000,$(1)),$(2)) @$(call emit-line,$(wordlist 137001,137500,$(1)),$(2)) @$(call emit-line,$(wordlist 137501,138000,$(1)),$(2)) @$(call emit-line,$(wordlist 138001,138500,$(1)),$(2)) @$(call emit-line,$(wordlist 138501,139000,$(1)),$(2)) @$(call emit-line,$(wordlist 139001,139500,$(1)),$(2)) @$(call emit-line,$(wordlist 139501,140000,$(1)),$(2)) @$(call emit-line,$(wordlist 140001,140500,$(1)),$(2)) @$(call emit-line,$(wordlist 140501,141000,$(1)),$(2)) @$(call emit-line,$(wordlist 141001,141500,$(1)),$(2)) @$(call emit-line,$(wordlist 141501,142000,$(1)),$(2)) @$(call emit-line,$(wordlist 142001,142500,$(1)),$(2)) @$(call emit-line,$(wordlist 142501,143000,$(1)),$(2)) @$(call emit-line,$(wordlist 143001,143500,$(1)),$(2)) @$(call emit-line,$(wordlist 143501,144000,$(1)),$(2)) @$(call emit-line,$(wordlist 144001,144500,$(1)),$(2)) @$(call emit-line,$(wordlist 144501,145000,$(1)),$(2)) @$(call emit-line,$(wordlist 145001,145500,$(1)),$(2)) @$(call emit-line,$(wordlist 145501,146000,$(1)),$(2)) @$(call emit-line,$(wordlist 146001,146500,$(1)),$(2)) @$(call emit-line,$(wordlist 146501,147000,$(1)),$(2)) @$(call emit-line,$(wordlist 147001,147500,$(1)),$(2)) @$(call emit-line,$(wordlist 147501,148000,$(1)),$(2)) @$(call emit-line,$(wordlist 148001,148500,$(1)),$(2)) @$(call emit-line,$(wordlist 148501,149000,$(1)),$(2)) @$(call emit-line,$(wordlist 149001,149500,$(1)),$(2)) @$(call emit-line,$(wordlist 149501,150000,$(1)),$(2)) @$(if $(wordlist 150001,150002,$(1)),$(error dump-words-to-file: Too many words ($(words $(1))))) endef # Return jar arguments to compress files in a given directory # $(1): directory # # Returns an @-file argument that contains the output of a subshell # that looks like -C $(1) path/to/file1 -C $(1) path/to/file2 # Also adds "-C out/empty ." which avoids errors in jar when # there are no files in the directory. define jar-args-sorted-files-in-directory @<(find $(1) -type f | sort | $(JAR_ARGS) $(1); echo "-C $(EMPTY_DIRECTORY) .") endef # append additional Java sources(resources/Proto sources, and etc) to $(1). define fetch-additional-java-source $(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \ find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' -and -not -name '.*' >> $(1); \ fi endef # Some historical notes: # - below we write the list of java files to java-source-list to avoid argument # list length problems with Cygwin # - we filter out duplicate java file names because eclipse's compiler # doesn't like them. define write-java-source-list @echo "$($(PRIVATE_PREFIX)DISPLAY) Java source list: $(PRIVATE_MODULE)" $(hide) rm -f $@ $(call dump-words-to-file,$(sort $(PRIVATE_JAVA_SOURCES)),$@.tmp) $(call fetch-additional-java-source,$@.tmp) $(hide) tr ' ' '\n' < $@.tmp | $(NORMALIZE_PATH) | sort -u > $@ endef # Common definition to invoke javac on the host and target. # # $(1): javac # $(2): classpath_libs define compile-java $(hide) rm -f $@ $(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR) $(hide) mkdir -p $(dir $@) $(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(PRIVATE_ANNO_INTERMEDIATES_DIR) $(if $(PRIVATE_SRCJARS),\ $(ZIPSYNC) -d $(PRIVATE_SRCJAR_INTERMEDIATES_DIR) -l $(PRIVATE_SRCJAR_LIST_FILE) -f "*.java" $(PRIVATE_SRCJARS)) $(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) $(if $(PRIVATE_SRCJARS),-o -s $(PRIVATE_SRCJAR_LIST_FILE) )] ; then \ $(SOONG_JAVAC_WRAPPER) $(JAVAC_WRAPPER) $(1) -encoding UTF-8 \ $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \ $(if $(PRIVATE_USE_SYSTEM_MODULES), \ $(addprefix --system=,$(PRIVATE_SYSTEM_MODULES_DIR)), \ $(addprefix -bootclasspath ,$(strip \ $(call normalize-path-list,$(PRIVATE_BOOTCLASSPATH)) \ $(PRIVATE_EMPTY_BOOTCLASSPATH)))) \ $(if $(PRIVATE_USE_SYSTEM_MODULES), \ $(if $(PRIVATE_PATCH_MODULE), \ --patch-module=$(PRIVATE_PATCH_MODULE)=$(call normalize-path-list,. $(2)))) \ $(addprefix -classpath ,$(call normalize-path-list,$(strip \ $(if $(PRIVATE_USE_SYSTEM_MODULES), \ $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \ $(2)))) \ $(if $(findstring true,$(PRIVATE_WARNINGS_ENABLE)),$(xlint_unchecked),) \ -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) -s $(PRIVATE_ANNO_INTERMEDIATES_DIR) \ $(PRIVATE_JAVACFLAGS) \ \@$(PRIVATE_JAVA_SOURCE_LIST) \ $(if $(PRIVATE_SRCJARS),\@$(PRIVATE_SRCJAR_LIST_FILE)) \ || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 ) \ fi $(if $(PRIVATE_JAR_EXCLUDE_FILES), $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) \ -name $(word 1, $(PRIVATE_JAR_EXCLUDE_FILES)) \ $(addprefix -o -name , $(wordlist 2, 999, $(PRIVATE_JAR_EXCLUDE_FILES))) \ | xargs rm -rf) $(if $(PRIVATE_JAR_PACKAGES), \ $(hide) find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -mindepth 1 -type f \ $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \ -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))/\*) -delete ; \ find $(PRIVATE_CLASS_INTERMEDIATES_DIR) -empty -delete) $(if $(PRIVATE_JAR_EXCLUDE_PACKAGES), $(hide) rm -rf \ $(foreach pkg, $(PRIVATE_JAR_EXCLUDE_PACKAGES), \ $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg)))) $(hide) $(SOONG_ZIP) -jar -o $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) -D $(PRIVATE_CLASS_INTERMEDIATES_DIR) $(if $(PRIVATE_EXTRA_JAR_ARGS),$(call add-java-resources-to,$@)) endef define transform-java-to-header.jar @echo "$($(PRIVATE_PREFIX)DISPLAY) Turbine: $(PRIVATE_MODULE)" @mkdir -p $(dir $@) @rm -rf $(dir $@)/classes-turbine @mkdir $(dir $@)/classes-turbine $(hide) if [ -s $(PRIVATE_JAVA_SOURCE_LIST) -o -n "$(PRIVATE_SRCJARS)" ] ; then \ $(JAVA) -jar $(TURBINE) \ --output $@.premerged --temp_dir $(dir $@)/classes-turbine \ --sources \@$(PRIVATE_JAVA_SOURCE_LIST) --source_jars $(PRIVATE_SRCJARS) \ --javacopts $(PRIVATE_JAVACFLAGS) $(COMMON_JDK_FLAGS) -- \ $(if $(PRIVATE_USE_SYSTEM_MODULES), \ --system $(PRIVATE_SYSTEM_MODULES_DIR), \ --bootclasspath $(strip $(PRIVATE_BOOTCLASSPATH))) \ --classpath $(strip $(if $(PRIVATE_USE_SYSTEM_MODULES), \ $(filter-out $(PRIVATE_SYSTEM_MODULES_LIBS),$(PRIVATE_BOOTCLASSPATH))) \ $(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) \ || ( rm -rf $(dir $@)/classes-turbine ; exit 41 ) && \ $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $@.premerged $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \ else \ $(MERGE_ZIPS) -j --ignore-duplicates -stripDir META-INF $@.tmp $(PRIVATE_STATIC_JAVA_HEADER_LIBRARIES) ; \ fi $(hide) $(ZIPTIME) $@.tmp $(hide) $(call commit-change-for-toc,$@) endef # Runs jarjar on an input file. Jarjar doesn't exit with a nonzero return code # when there is a syntax error in a rules file and doesn't write the output # file, so removes the output file before running jarjar and check if it exists # after running jarjar. define transform-jarjar echo $($(PRIVATE_PREFIX)DISPLAY) JarJar: $@ rm -f $@ $(JAVA) -jar $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@ [ -e $@ ] || (echo "Missing output file"; exit 1) endef # Moves $1.tmp to $1 if necessary. This is designed to be used with # .KATI_RESTAT. For kati, this function doesn't update the timestamp # of $1 when $1.tmp is identical to $1 so that ninja won't rebuild # targets which depend on $1. define commit-change-for-toc $(hide) if cmp -s $1.tmp $1 ; then \ rm $1.tmp ; \ else \ mv $1.tmp $1 ; \ fi endef ifeq (,$(TARGET_BUILD_APPS)) ## Rule to create a table of contents from a .dex file. ## Must be called with $(eval). # $(1): The directory which contains classes*.dex files define _transform-dex-to-toc $1/classes.dex.toc: PRIVATE_INPUT_DEX_FILES := $1/classes*.dex $1/classes.dex.toc: $1/classes.dex $(DEXDUMP) @echo Generating TOC: $$@ $(hide) ANDROID_LOG_TAGS="*:e" $(DEXDUMP) -l xml $$(PRIVATE_INPUT_DEX_FILES) > $$@.tmp $$(call commit-change-for-toc,$$@) endef ## Define a rule which generates .dex.toc and mark it as .KATI_RESTAT. # $(1): The directory which contains classes*.dex files define define-dex-to-toc-rule $(eval $(call _transform-dex-to-toc,$1))\ $(eval .KATI_RESTAT: $1/classes.dex.toc) endef else # Turn off .toc optimization for apps build as we cannot build dexdump. define define-dex-to-toc-rule endef endif # TARGET_BUILD_APPS # Takes an sdk version that might be PLATFORM_VERSION_CODENAME (for example P), # returns a number greater than the highest existing sdk version if it is, or # the input if it is not. define codename-or-sdk-to-sdk $(if $(filter $(1),$(PLATFORM_VERSION_CODENAME)),10000,$(1)) endef # Uses LOCAL_SDK_VERSION and PLATFORM_SDK_VERSION to determine a compileSdkVersion # in the form of a number or a codename (28 or P) define module-sdk-version $(strip \ $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \ $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \ $(PLATFORM_SDK_VERSION))) endef # Uses LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine # a targetSdkVersion in the form of a number or a codename (28 or P). define module-target-sdk-version $(strip \ $(if $(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION)), \ $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)), \ $(DEFAULT_APP_TARGET_SDK))) endef # Uses LOCAL_MIN_SDK_VERSION, LOCAL_SDK_VERSION and DEFAULT_APP_TARGET_SDK to determine # a minSdkVersion in the form of a number or a codename (28 or P). define module-min-sdk-version $(if $(LOCAL_MIN_SDK_VERSION),$(LOCAL_MIN_SDK_VERSION),$(call module-target-sdk-version)) endef # Checks if module is in vendor or product define module-in-vendor-or-product $(if $(filter true,$(LOCAL_IN_VENDOR) $(LOCAL_IN_PRODUCT)),true) endef define transform-classes.jar-to-dex @echo "target Dex: $(PRIVATE_MODULE)" @mkdir -p $(dir $@)tmp $(hide) rm -f $(dir $@)classes*.dex $(dir $@)d8_input.jar $(hide) $(ZIP2ZIP) -j -i $< -o $(dir $@)d8_input.jar "**/*.class" $(hide) $(D8_WRAPPER) $(D8_COMMAND) \ --output $(dir $@)tmp \ $(addprefix --lib ,$(PRIVATE_D8_LIBS)) \ --min-api $(PRIVATE_MIN_SDK_VERSION) \ $(subst --main-dex-list=, --main-dex-list , \ $(filter-out --core-library --multi-dex --minimal-main-dex,$(PRIVATE_DX_FLAGS))) \ $(dir $@)d8_input.jar $(hide) mv $(dir $@)tmp/* $(dir $@) $(hide) rm -f $(dir $@)d8_input.jar $(hide) rm -rf $(dir $@)tmp endef # We need the extra blank line, so that the command will be on a separate line. # $(1): the package # $(2): the ABI name # $(3): the list of shared libraies define _add-jni-shared-libs-to-package-per-abi $(hide) cp $(3) $(dir $(1))lib/$(2) endef # $(1): the package file # $(2): if true, uncompress jni libs define create-jni-shared-libs-package rm -rf $(dir $(1))lib mkdir -p $(addprefix $(dir $(1))lib/,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI)) $(foreach abi,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI),\ $(call _add-jni-shared-libs-to-package-per-abi,$(1),$(abi),\ $(patsubst $(abi):%,%,$(filter $(abi):%,$(PRIVATE_JNI_SHARED_LIBRARIES))))) $(SOONG_ZIP) $(if $(2),-L 0) -o $(1) -C $(dir $(1)) -D $(dir $(1))lib rm -rf $(dir $(1))lib endef # $(1): the jar file. # $(2): the classes.dex file. define create-dex-jar find $(dir $(2)) -maxdepth 1 -name "classes*.dex" | sort > $(1).lst $(SOONG_ZIP) -o $(1) -C $(dir $(2)) -l $(1).lst endef # Add java resources added by the current module to an existing package. # $(1) destination package. define add-java-resources-to $(call _java-resources,$(1),u) endef # Add java resources added by the current module to a new jar. # $(1) destination jar. define create-java-resources-jar $(call _java-resources,$(1),c) endef define _java-resources $(call dump-words-to-file, $(PRIVATE_EXTRA_JAR_ARGS), $(1).jar-arg-list) $(hide) $(JAR) $(2)f $(1) @$(1).jar-arg-list @rm -f $(1).jar-arg-list endef # Add resources (non .class files) from a jar to a package # $(1): the package file # $(2): the jar file # $(3): temporary directory define add-jar-resources-to-package rm -rf $(3) mkdir -p $(3) zipinfo -1 $(2) > /dev/null unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E "\.class$$") $(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3)) endef # $(1): the output resources jar. # $(2): the input jar define extract-resources-jar $(ZIP2ZIP) -i $(2) -o $(1) -x '**/*.class' -x '**/*/' endef # Sign a package using the specified key/cert. # define sign-package $(call sign-package-arg,$@) endef # $(1): the package file we are signing. define sign-package-arg $(hide) mv $(1) $(1).unsigned $(hide) $(JAVA) -Djava.library.path=$$(dirname $(SIGNAPK_JNI_LIBRARY_PATH)) -jar $(SIGNAPK_JAR) \ $(if $(strip $(PRIVATE_CERTIFICATE_LINEAGE)), --lineage $(PRIVATE_CERTIFICATE_LINEAGE)) \ $(if $(strip $(PRIVATE_ROTATION_MIN_SDK_VERSION)), --rotation-min-sdk-version $(PRIVATE_ROTATION_MIN_SDK_VERSION)) \ $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) \ $(PRIVATE_ADDITIONAL_CERTIFICATES) $(1).unsigned $(1).signed $(hide) mv $(1).signed $(1) endef # Align STORED entries of a package on 4-byte boundaries to make them easier to mmap. # define align-package $(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \ mv $@ $@.unaligned; \ $(ZIPALIGN) \ -f \ -p \ 4 \ $@.unaligned $@.aligned; \ mv $@.aligned $@; \ fi endef # Verifies ZIP alignment of a package. # define check-package-alignment $(hide) if ! $(ZIPALIGN) -c -p 4 $@ >/dev/null ; then \ $(call echo-error,$@,Improper package alignment); \ exit 1; \ fi endef # Compress a package using the standard gzip algorithm. define compress-package $(hide) \ mv $@ $@.uncompressed; \ $(GZIP) -9 -c $@.uncompressed > $@.compressed; \ rm -f $@.uncompressed; \ mv $@.compressed $@; endef ifeq ($(HOST_OS),linux) # Runs appcompat and store logs in $(PRODUCT_OUT)/appcompat define extract-package $(AAPT2) dump resources $@ | awk -F ' |=' '/^Package/{print $$3; exit}' >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && endef define appcompat-header $(hide) \ mkdir -p $(PRODUCT_OUT)/appcompat && \ rm -f $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ echo -n "Package name: " >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ $(extract-package) \ echo "Module name in Android tree: $(PRIVATE_MODULE)" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ echo "Local path in Android tree: $(PRIVATE_PATH)" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ echo "Install path: $(patsubst $(PRODUCT_OUT)/%,%,$(PRIVATE_INSTALLED_MODULE))" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ echo >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log endef ART_VERIDEX_APPCOMPAT:=$(HOST_OUT)/bin/appcompat define run-appcompat $(hide) \ echo "appcompat output:" >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log && \ ANDROID_LOG_TAGS="*:e" $(ART_VERIDEX_APPCOMPAT) --dex-file=$@ 2>&1 >> $(PRODUCT_OUT)/appcompat/$(PRIVATE_MODULE).log endef appcompat-files = \ $(AAPT2) \ $(ART_VERIDEX_APPCOMPAT) \ else appcompat-header = run-appcompat = appcompat-files = endif # HOST_OS == linux .KATI_READONLY: appcompat-header run-appcompat appcompat-files # Remove dynamic timestamps from packages # define remove-timestamps-from-package $(hide) $(ZIPTIME) $@ endef # Uncompress dex files embedded in an apk. # define uncompress-dexs if (zipinfo $@ '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ $(ZIP2ZIP) -i $@ -o $@.tmp -0 "classes*.dex" && \ mv -f $@.tmp $@ ; \ fi endef # Uncompress shared JNI libraries embedded in an apk. # define uncompress-prebuilt-embedded-jni-libs if (zipinfo $@ 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ $(ZIP2ZIP) -i $@ -o $@.tmp -0 'lib/**/*.so' && mv -f $@.tmp $@ ; \ fi endef # Verifies shared JNI libraries and dex files in an apk are uncompressed. # define check-jni-dex-compression if (zipinfo $@ 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ $(call echo-error,$@,Contains compressed JNI libraries and/or dex files); \ exit 1; \ fi endef # Remove unwanted shared JNI libraries embedded in an apk. # define remove-unwanted-prebuilt-embedded-jni-libs $(if $(PRIVATE_EMBEDDED_JNI_LIBS), \ $(ZIP2ZIP) -i $@ -o $@.tmp \ -x 'lib/**/*.so' $(addprefix -X ,$(PRIVATE_EMBEDDED_JNI_LIBS)) && \ mv -f $@.tmp $@) endef # TODO(joeo): If we can ever upgrade to post 3.81 make and get the # new prebuilt rules to work, we should change this to copy the # resources to the out directory and then copy the resources. # Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR # in transform-java-to-classes for the sake of vm-tests. define transform-host-java-to-package @echo "Host Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))" $(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_LIBRARIES)) endef # Note: we intentionally don't clean PRIVATE_CLASS_INTERMEDIATES_DIR # in transform-java-to-classes for the sake of vm-tests. define transform-host-java-to-dalvik-package @echo "Dalvik Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))" $(call compile-java,$(HOST_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) endef ########################################################### ## Commands for copying files ########################################################### # Define a rule to copy a header. Used via $(eval) by copy_headers.make. # $(1): source header # $(2): destination header define copy-one-header $(2): $(1) @echo "Header: $$@" $$(copy-file-to-new-target-with-cp) endef # Define a rule to copy a file. For use via $(eval). # $(1): source file # $(2): destination file define copy-one-file $(2): $(1) @echo "Copy: $$@" $$(copy-file-to-target) endef # Define a rule to copy a license metadata file. For use via $(eval). # $(1): source license metadata file # $(2): destination license metadata file # $(3): built targets # $(4): installed targets define copy-one-license-metadata-file $(2): PRIVATE_BUILT=$(3) $(2): PRIVATE_INSTALLED=$(4) $(2): $(1) @echo "Copy: $$@" $$(call copy-license-metadata-file-to-target,$$(PRIVATE_BUILT),$$(PRIVATE_INSTALLED)) endef define copy-and-uncompress-dexs $(2): $(1) $(ZIPALIGN) $(ZIP2ZIP) @echo "Uncompress dexs in: $$@" $$(copy-file-to-target) $$(uncompress-dexs) $$(align-package) endef # Create copy pair for compatibility suite # Filter out $(LOCAL_INSTALLED_MODULE) to prevent overriding target # $(1): source path # $(2): destination path # The format of copy pair is src:dst define compat-copy-pair $(if $(filter-out $(2), $(LOCAL_INSTALLED_MODULE)), $(1):$(2)) endef # Create copy pair for $(1) $(2) # If $(2) is substring of $(3) do nothing. # $(1): source path # $(2): destination path # $(3): filter-out target # The format of copy pair is src:dst define filter-copy-pair $(if $(findstring $(2), $(3)),,$(1):$(2)) endef # Copies many files. # $(1): The files to copy. Each entry is a ':' separated src:dst pair # $(2): An optional directory to prepend to the destination # Evaluates to the list of the dst files (ie suitable for a dependency list) define copy-many-files $(foreach f, $(1), $(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ $(if $(strip $(2)), \ $(eval _cmf_dest := $(patsubst %/,%,$(strip $(2)))/$(patsubst /%,%,$(_cmf_dest)))) \ $(if $(filter-out $(_cmf_src), $(_cmf_dest)), \ $(eval $(call copy-one-file,$(_cmf_src),$(_cmf_dest)))) \ $(_cmf_dest))) endef # Copy the file only if it's a well-formed init script file. For use via $(eval). # $(1): source file # $(2): destination file define copy-init-script-file-checked ifdef TARGET_BUILD_UNBUNDLED # TODO (b/185624993): Remove the check on TARGET_BUILD_UNBUNDLED when host_init_verifier can run # without requiring the HIDL interface map. $(2): $(1) else ifneq ($(HOST_OS),darwin) # Host init verifier doesn't exist on darwin. $(2): \ $(1) \ $(HOST_INIT_VERIFIER) \ $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ $(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ $(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ $(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ $(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ $(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts $(hide) $(HOST_INIT_VERIFIER) \ -p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ -p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ -p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ -p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ -p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ --property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \ $$< else $(2): $(1) endif @echo "Copy init script: $$@" $$(copy-file-to-target) endef # Copies many init script files and check they are well-formed. # $(1): The init script files to copy. Each entry is a ':' separated src:dst pair. define copy-many-init-script-files-checked $(foreach f, $(1), $(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ $(eval $(call copy-init-script-file-checked,$(_cmf_src),$(_cmf_dest))))) endef # Copy the file only if it's a well-formed xml file. For use via $(eval). # $(1): source file # $(2): destination file, must end with .xml. define copy-xml-file-checked $(2): $(1) $(XMLLINT) @echo "Copy xml: $$@" $(hide) $(XMLLINT) $$< >/dev/null # Don't print the xml file to stdout. $$(copy-file-to-target) endef # Copies many xml files and check they are well-formed. # $(1): The xml files to copy. Each entry is a ':' separated src:dst pair. # Evaluates to the list of the dst files. (ie suitable for a dependency list.) define copy-many-xml-files-checked $(foreach f, $(1), $(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ $(eval $(call copy-xml-file-checked,$(_cmf_src),$(_cmf_dest))) \ $(_cmf_dest))) endef # Copy the file only if it is a well-formed manifest file. For use viea $(eval) # $(1): source file # $(2): destination file define copy-vintf-manifest-checked $(2): $(1) $(HOST_OUT_EXECUTABLES)/assemble_vintf @echo "Copy xml: $$@" $(hide) mkdir -p "$$(dir $$@)" $(hide) VINTF_IGNORE_TARGET_FCM_VERSION=true\ $(HOST_OUT_EXECUTABLES)/assemble_vintf -i $$< -o $$@ endef # Copies many vintf manifest files checked. # $(1): The files to copy. Each entry is a ':' separated src:dst pair define copy-many-vintf-manifest-files-checked $(foreach f, $(1), $(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 2,$(_cmf_tuple))) \ $(eval $(call copy-vintf-manifest-checked,$(_cmf_src),$(_cmf_dest))))) endef # Copy the file only if it's not an ELF file. For use via $(eval). # $(1): source file # $(2): destination file # $(3): message to print on error define copy-non-elf-file-checked $(eval check_non_elf_file_timestamp := \ $(call intermediates-dir-for,FAKE,check-non-elf-file-timestamps)/$(2).timestamp) $(check_non_elf_file_timestamp): $(1) $(LLVM_READOBJ) @echo "Check non-ELF: $$<" $(hide) mkdir -p "$$(dir $$@)" $(hide) rm -f "$$@" $(hide) \ if $(LLVM_READOBJ) -h "$$<" 2>/dev/null | grep -q "^Format: elf"; then \ $(call echo-error,$(2),$(3)); \ $(call echo-error,$(2),found ELF file: $$<); \ false; \ fi $(hide) touch "$$@" $(2): $(1) $(check_non_elf_file_timestamp) @echo "Copy non-ELF: $$@" $$(copy-file-to-target) check-elf-prebuilt-product-copy-files: $(check_non_elf_file_timestamp) endef # The -t option to acp and the -p option to cp is # required for OSX. OSX has a ridiculous restriction # where it's an error for a .a file's modification time # to disagree with an internal timestamp, and this # macro is used to install .a files (among other things). # Copy a single file from one place to another, # preserving permissions and overwriting any existing # file. # When we used acp, it could not handle high resolution timestamps # on file systems like ext4. Because of that, '-t' option was disabled # and copy-file-to-target was identical to copy-file-to-new-target. # Keep the behavior until we audit and ensure that switching this back # won't break anything. define copy-file-to-target @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cp "$<" "$@" endef # Same as copy-file-to-target, but assume file is a licenes metadata file, # and append built from $(1) and installed from $(2). define copy-license-metadata-file-to-target @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cp "$<" "$@" $(strip \ $(foreach b,$(1), && (grep -F 'built: "'"$(b)"'"' "$@" >/dev/null || echo 'built: "'"$(b)"'"' >>"$@")) \ $(foreach i,$(2), && (grep -F 'installed: "'"$(i)"'"' "$@" >/dev/null || echo 'installed: "'"$(i)"'"' >>"$@")) \ ) endef # The same as copy-file-to-target, but use the local # cp command instead of acp. define copy-file-to-target-with-cp @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cp -p "$<" "$@" endef # The same as copy-file-to-target, but don't preserve # the old modification time. define copy-file-to-new-target @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cp $< $@ endef # The same as copy-file-to-new-target, but use the local # cp command instead of acp. define copy-file-to-new-target-with-cp @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) cp $< $@ endef # The same as copy-file-to-new-target, but preserve symlinks. Symlinks are # converted to absolute to not break. define copy-file-or-link-to-new-target @mkdir -p $(dir $@) $(hide) rm -f $@ $(hide) if [ -h $< ]; then \ ln -s $$(realpath $<) $@; \ else \ cp $< $@; \ fi endef # Copy a prebuilt file to a target location. define transform-prebuilt-to-target @echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)" $(copy-file-to-target) endef # Copy a prebuilt file to a target location, but preserve symlinks rather than # dereference them. define copy-or-link-prebuilt-to-target @echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)" $(copy-file-or-link-to-new-target) endef # Copy a list of files/directories to target location, with sub dir structure preserved. # For example $(HOST_OUT_EXECUTABLES)/aapt -> $(staging)/bin/aapt . # $(1): the source list of files/directories. # $(2): the path prefix to strip. In the above example it would be $(HOST_OUT). # $(3): the target location. define copy-files-with-structure $(foreach t,$(1),\ $(eval s := $(patsubst $(2)%,%,$(t)))\ $(hide) mkdir -p $(dir $(3)/$(s)); cp -Rf $(t) $(3)/$(s)$(newline)) endef # Define a rule to create a symlink to a file. # $(1): any dependencies # $(2): source (may be relative) # $(3): full path to destination define symlink-file $(eval $(_symlink-file)) $(eval $(call declare-license-metadata,$(3),,,,,,)) $(eval $(call declare-license-deps,$(3),$(1))) endef define _symlink-file $(3): $(1) @echo "Symlink: $$@ -> $(2)" @mkdir -p $$(dir $$@) @rm -rf $$@ $(hide) ln -sf $(2) $$@ endef # Copy an apk to a target location while removing classes*.dex # $(1): source file # $(2): destination file # $(3): LOCAL_STRIP_DEX, if non-empty then strip classes*.dex define dexpreopt-copy-jar $(2): $(1) @echo "Copy: $$@" $$(copy-file-to-target) $(if $(3),$$(call dexpreopt-remove-classes.dex,$$@)) endef # $(1): the .jar or .apk to remove classes.dex. Note that if all dex files # are uncompressed in the archive, then dexopt will not do a copy of the dex # files and we should not strip. define dexpreopt-remove-classes.dex $(hide) if (zipinfo $1 '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then \ zip --quiet --delete $(1) classes.dex; \ dex_index=2; \ while zip --quiet --delete $(1) classes$${dex_index}.dex > /dev/null; do \ let dex_index=dex_index+1; \ done \ fi endef # Copy an unstripped binary to the symbols directory while also extracting # a hash mapping to the mapping directory. # $(1): unstripped intermediates file # $(2): path in symbols directory # $(3): path in elf_symbol_mapping packaging directory define copy-unstripped-elf-file-with-mapping $(call _copy-symbols-file-with-mapping,$(1),$(2),elf,$(3)) endef # Copy an R8 dictionary to the packaging directory while also extracting # a hash mapping to the mapping directory. # $(1): unstripped intermediates file # $(2): path in packaging directory # $(3): path in mappings packaging directory define copy-r8-dictionary-file-with-mapping $(call _copy-symbols-file-with-mapping,$(1),$(2),r8,$(3)) endef # Copy an unstripped binary or R8 dictionary to the symbols directory # while also extracting a hash mapping to the mapping directory. # $(1): unstripped intermediates file # $(2): path in symbols directory # $(3): file type (elf or r8) # $(4): path in the mappings directory # # Regarding the restats at the end: I think you should only need to use KATI_RESTAT on $(2), but # there appears to be a bug in kati where it was not adding restat=true in the ninja file unless we # also added 4 to KATI_RESTAT. define _copy-symbols-file-with-mapping $(2): .KATI_IMPLICIT_OUTPUTS := $(4) $(2): $(SYMBOLS_MAP) $(2): $(1) @echo "Copy symbols with mapping: $$@" $$(copy-file-to-target) $(SYMBOLS_MAP) -$(strip $(3)) $(2) -write_if_changed $(4) .KATI_RESTAT: $(2) .KATI_RESTAT: $(4) endef ########################################################### ## Commands to call R8 ########################################################### # Use --debug flag for eng builds by default ifeq (eng,$(TARGET_BUILD_VARIANT)) R8_DEBUG_MODE := --debug else R8_DEBUG_MODE := endif define transform-jar-to-dex-r8 @echo R8: $@ $(hide) rm -f $(PRIVATE_PROGUARD_DICTIONARY) $(hide) $(R8_WRAPPER) $(R8_COMMAND) \ -injars '$<' \ --min-api $(PRIVATE_MIN_SDK_VERSION) \ --no-data-resources \ --force-proguard-compatibility --output $(subst classes.dex,,$@) \ $(R8_DEBUG_MODE) \ $(PRIVATE_PROGUARD_FLAGS) \ $(addprefix -injars , $(PRIVATE_EXTRA_INPUT_JAR)) \ $(PRIVATE_DX_FLAGS) \ -ignorewarnings $(hide) touch $(PRIVATE_PROGUARD_DICTIONARY) endef ########################################################### ## Stuff source generated from one-off tools ########################################################### define transform-generated-source @echo "$($(PRIVATE_PREFIX)DISPLAY) Generated: $(PRIVATE_MODULE) <= $<" @mkdir -p $(dir $@) $(hide) $(PRIVATE_CUSTOM_TOOL) endef ########################################################### ## Assertions about attributes of the target ########################################################### # $(1): The file to check define get-file-size stat -c "%s" "$(1)" | tr -d '\n' endef # $(1): The file(s) to check (often $@) # $(2): The partition size. define assert-max-image-size $(if $(2), \ size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \ total=$$(( $$( echo "$$size" ) )); \ printname=$$(echo -n "$(1)" | tr " " +); \ maxsize=$$(($(2))); \ if [ "$$total" -gt "$$maxsize" ]; then \ echo "error: $$printname too large ($$total > $$maxsize)"; \ false; \ elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \ echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \ fi \ , \ true \ ) endef ########################################################### ## Define device-specific radio files ########################################################### INSTALLED_RADIOIMAGE_TARGET := # Copy a radio image file to the output location, and add it to # INSTALLED_RADIOIMAGE_TARGET. # $(1): filename define add-radio-file $(eval $(call add-radio-file-internal,$(1),$(notdir $(1)))) endef define add-radio-file-internal INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2) $$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1) $$(transform-prebuilt-to-target) endef # Version of add-radio-file that also arranges for the version of the # file to be checked against the contents of # $(TARGET_BOARD_INFO_FILE). # $(1): filename # $(2): name of version variable in board-info (eg, "version-baseband") define add-radio-file-checked $(eval $(call add-radio-file-checked-internal,$(1),$(notdir $(1)),$(2))) endef define add-radio-file-checked-internal INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(2) BOARD_INFO_CHECK += $(3):$(LOCAL_PATH)/$(1) $$(PRODUCT_OUT)/$(2) : $$(LOCAL_PATH)/$(1) $$(transform-prebuilt-to-target) endef ## Whether to build from source if prebuilt alternative exists ########################################################### # $(1): module name # $(2): LOCAL_PATH # Expands to empty string if not from source. ifeq (true,$(ANDROID_BUILD_FROM_SOURCE)) define if-build-from-source true endef else define if-build-from-source $(if $(filter $(ANDROID_NO_PREBUILT_MODULES),$(1))$(filter \ $(addsuffix %,$(ANDROID_NO_PREBUILT_PATHS)),$(2)),true) endef endif # Include makefile $(1) if build from source for module $(2) # $(1): the makefile to include # $(2): module name # $(3): LOCAL_PATH define include-if-build-from-source $(if $(call if-build-from-source,$(2),$(3)),$(eval include $(1))) endef # Return the arch for the source file of a prebuilt # Return "none" if no matching arch found and return empty # if the input is empty, so the result can be passed to # LOCAL_MODULE_TARGET_ARCH. # $(1) the list of archs supported by the prebuilt define get-prebuilt-src-arch $(strip $(if $(filter $(TARGET_ARCH),$(1)),$(TARGET_ARCH),\ $(if $(filter $(TARGET_2ND_ARCH),$(1)),$(TARGET_2ND_ARCH),$(if $(1),none)))) endef # ############################################################### # Set up statistics gathering # ############################################################### STATS.MODULE_TYPE := \ HOST_STATIC_LIBRARY \ HOST_SHARED_LIBRARY \ STATIC_LIBRARY \ SHARED_LIBRARY \ EXECUTABLE \ HOST_EXECUTABLE \ PACKAGE \ PHONY_PACKAGE \ HOST_PREBUILT \ PREBUILT \ MULTI_PREBUILT \ JAVA_LIBRARY \ STATIC_JAVA_LIBRARY \ HOST_JAVA_LIBRARY \ DROIDDOC \ COPY_HEADERS \ NATIVE_TEST \ NATIVE_BENCHMARK \ HOST_NATIVE_TEST \ FUZZ_TEST \ HOST_FUZZ_TEST \ STATIC_TEST_LIBRARY \ HOST_STATIC_TEST_LIBRARY \ NOTICE_FILE \ base_rules \ HEADER_LIBRARY \ HOST_TEST_CONFIG \ TARGET_TEST_CONFIG $(foreach s,$(STATS.MODULE_TYPE),$(eval STATS.MODULE_TYPE.$(s) :=)) define record-module-type $(strip $(if $(LOCAL_RECORDED_MODULE_TYPE),, $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)), $(if $(filter $(1),$(STATS.MODULE_TYPE)), $(eval LOCAL_RECORDED_MODULE_TYPE := true) $(eval STATS.MODULE_TYPE.$(1) += 1), $(error Invalid module type: $(1)))))) endef ########################################################### ## Compatibility suite tools ########################################################### # Return a list of output directories for a given suite and the current LOCAL_MODULE. # Can be passed a subdirectory to use for the common testcase directory. define compatibility_suite_dirs $(strip \ $(if $(COMPATIBILITY_TESTCASES_OUT_$(1)), \ $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1))$(LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY),\ $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\ $(COMPATIBILITY_TESTCASES_OUT_$(1)))) \ $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(2)) endef # For each suite: # 1. Copy the files to the many suite output directories. # And for test config files, we'll check the .xml is well-formed before copy. # 2. Add all the files to each suite's dependent files list. # 3. Do the dependency addition to my_all_targets. # 4. Save the module name to COMPATIBILITY.$(suite).MODULES for each suite. # 5. Collect files to dist to ALL_COMPATIBILITY_DIST_FILES. # Requires for each suite: use my_compat_dist_config_$(suite) to define the test config. # and use my_compat_dist_$(suite) to define the others. define create-suite-dependencies $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval $(if $(strip $(module_license_metadata)),\ $$(foreach f,$$(my_compat_dist_$(suite)),$$(call declare-copy-target-license-metadata,$$(call word-colon,2,$$(f)),$$(call word-colon,1,$$(f)))),\ $$(eval my_test_data += $$(my_compat_dist_$(suite))) \ )) \ $(eval $(if $(strip $(module_license_metadata)),\ $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call declare-copy-target-license-metadata,$$(call word-colon,2,$$(f)),$$(call word-colon,1,$$(f)))),\ $$(eval my_test_config += $$(my_compat_dist_config_$(suite))) \ )) \ $(if $(filter $(suite),$(ALL_COMPATIBILITY_SUITES)),,\ $(eval ALL_COMPATIBILITY_SUITES += $(suite)) \ $(eval COMPATIBILITY.$(suite).FILES :=) \ $(eval COMPATIBILITY.$(suite).MODULES :=) \ $(eval COMPATIBILITY.$(suite).API_MAP_FILES :=)) \ $(eval COMPATIBILITY.$(suite).FILES += \ $$(foreach f,$$(my_compat_dist_$(suite)),$$(call word-colon,2,$$(f))) \ $$(foreach f,$$(my_compat_dist_config_$(suite)),$$(call word-colon,2,$$(f))) \ $$(my_compat_dist_test_data_$(suite))) \ $(eval COMPATIBILITY.$(suite).ARCH_DIRS.$(my_register_name) := $(my_compat_module_arch_dir_$(suite).$(my_register_name))) \ $(eval COMPATIBILITY.$(suite).API_MAP_FILES += $$(my_compat_api_map_$(suite))) \ $(eval COMPATIBILITY.$(suite).SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES += $(LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) \ $(eval ALL_COMPATIBILITY_DIST_FILES += $$(my_compat_dist_$(suite))) \ $(eval COMPATIBILITY.$(suite).MODULES += $$(my_register_name))) \ $(eval $(my_all_targets) : \ $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE), \ $(foreach f,$(my_compat_dist_$(suite)), $(call word-colon,2,$(f))))) \ $(call copy-many-xml-files-checked, \ $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE),$(my_compat_dist_config_$(suite)))))) endef # Define symbols.zip and symbols-mapping.textproto build rule per test suite # # $(1): Name of the test suite to create the zip and mapping build rules define create-suite-symbols-map _suite_symbols_zip := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols.zip) _suite_symbols_mapping := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols-mapping.textproto) _suite_modules_symbols_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).SYMBOLIC_OUTPUT_PATH)) _suite_modules_mapping_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).ELF_SYMBOL_MAPPING_PATH)) $$(_suite_symbols_zip): PRIVATE_SUITE_SYMBOLS_MAPPING := $$(_suite_symbols_mapping) $$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_SYMBOLS_FILES := $$(_suite_modules_symbols_files) $$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_MAPPING_FILES := $$(_suite_modules_mapping_files) $$(_suite_symbols_zip): $$(SOONG_ZIP) $$(SYMBOLS_MAP) $$(_suite_modules_symbols_files) $$(_suite_modules_mapping_files) @echo "Package $(1) symbols: $$@" $(hide) rm -rf $$@ $$@.symbols_list $$@.mapping_list echo "$$(PRIVATE_SUITE_MODULES_SYMBOLS_FILES)" | tr " " "\n" | sort > $$@.symbols_list $(hide) $$(SOONG_ZIP) -d -o $$@ -l $$@.symbols_list echo "$$(PRIVATE_SUITE_MODULES_MAPPING_FILES)" | tr " " "\n" | sort > $$@.mapping_list $(hide) $$(SYMBOLS_MAP) -merge $$(PRIVATE_SUITE_SYMBOLS_MAPPING) @$$@.mapping_list $$(_suite_symbols_zip): .KATI_IMPLICIT_OUTPUTS := $$(_suite_symbols_mapping) .PHONY: $(1) $(1): $$(_suite_symbols_zip) $$(_suite_symbols_mapping) $$(call dist-for-goals-with-filenametag,$(1), $$(_suite_symbols_zip) $$(_suite_symbols_mapping)) endef ########################################################### ## Path Cleaning ########################################################### # Remove "dir .." combinations (but keep ".. ..") # # $(1): The expanded path, where / is converted to ' ' to work with $(word) define _clean-path-strip-dotdot $(strip \ $(if $(word 2,$(1)), $(if $(call streq,$(word 2,$(1)),..), $(if $(call streq,$(word 1,$(1)),..), $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1))) , $(call _clean-path-strip-dotdot,$(wordlist 3,$(words $(1)),$(1))) ) , $(word 1,$(1)) $(call _clean-path-strip-dotdot,$(wordlist 2,$(words $(1)),$(1))) ) , $(1) ) ) endef # Remove any leading .. from the path (in case of /..) # # Should only be called if the original path started with / # $(1): The expanded path, where / is converted to ' ' to work with $(word) define _clean-path-strip-root-dotdots $(strip $(if $(call streq,$(firstword $(1)),..), $(call _clean-path-strip-root-dotdots,$(wordlist 2,$(words $(1)),$(1))), $(1))) endef # Call _clean-path-strip-dotdot until the path stops changing # $(1): Non-empty if this path started with a / # $(2): The expanded path, where / is converted to ' ' to work with $(word) define _clean-path-expanded $(strip \ $(eval _ep := $(call _clean-path-strip-dotdot,$(2))) $(if $(1),$(eval _ep := $(call _clean-path-strip-root-dotdots,$(_ep)))) $(if $(call streq,$(2),$(_ep)), $(_ep), $(call _clean-path-expanded,$(1),$(_ep)))) endef # Clean the file path -- remove //, dir/.., extra . # # This should be the same semantics as golang's filepath.Clean # # $(1): The file path to clean define clean-path $(strip \ $(if $(call streq,$(words $(1)),1), $(eval _rooted := $(filter /%,$(1))) $(eval _expanded_path := $(filter-out .,$(subst /,$(space),$(1)))) $(eval _path := $(if $(_rooted),/)$(subst $(space),/,$(call _clean-path-expanded,$(_rooted),$(_expanded_path)))) $(if $(_path), $(_path), . ) , $(if $(call streq,$(words $(1)),0), ., $(error Call clean-path with only one path (without spaces)) ) ) ) endef ifeq ($(TEST_MAKE_clean_path),true) define my_test $(if $(call streq,$(call clean-path,$(1)),$(2)),, $(eval my_failed := true) $(warning clean-path test '$(1)': expected '$(2)', got '$(call clean-path,$(1))')) endef my_failed := # Already clean $(call my_test,abc,abc) $(call my_test,abc/def,abc/def) $(call my_test,a/b/c,a/b/c) $(call my_test,.,.) $(call my_test,..,..) $(call my_test,../..,../..) $(call my_test,../../abc,../../abc) $(call my_test,/abc,/abc) $(call my_test,/,/) # Empty is current dir $(call my_test,,.) # Remove trailing slash $(call my_test,abc/,abc) $(call my_test,abc/def/,abc/def) $(call my_test,a/b/c/,a/b/c) $(call my_test,./,.) $(call my_test,../,..) $(call my_test,../../,../..) $(call my_test,/abc/,/abc) # Remove doubled slash $(call my_test,abc//def//ghi,abc/def/ghi) $(call my_test,//abc,/abc) $(call my_test,///abc,/abc) $(call my_test,//abc//,/abc) $(call my_test,abc//,abc) # Remove . elements $(call my_test,abc/./def,abc/def) $(call my_test,/./abc/def,/abc/def) $(call my_test,abc/.,abc) # Remove .. elements $(call my_test,abc/def/ghi/../jkl,abc/def/jkl) $(call my_test,abc/def/../ghi/../jkl,abc/jkl) $(call my_test,abc/def/..,abc) $(call my_test,abc/def/../..,.) $(call my_test,/abc/def/../..,/) $(call my_test,abc/def/../../..,..) $(call my_test,/abc/def/../../..,/) $(call my_test,abc/def/../../../ghi/jkl/../../../mno,../../mno) $(call my_test,/../abc,/abc) # Combinations $(call my_test,abc/./../def,def) $(call my_test,abc//./../def,def) $(call my_test,abc/../../././../def,../../def) ifdef my_failed $(error failed clean-path test) endif endif ########################################################### ## Given a filepath, returns nonempty if the path cannot be ## validated to be contained in the current directory ## This is, this function checks for '/' and '..' ## ## $(1): path to validate define try-validate-path-is-subdir $(strip \ $(if $(filter /%,$(1)), $(1) starts with a slash ) $(if $(filter ../%,$(call clean-path,$(1))), $(1) escapes its parent using '..' ) $(if $(strip $(1)), , '$(1)' is empty ) ) endef define validate-path-is-subdir $(if $(call try-validate-path-is-subdir,$(1)), $(call pretty-error, Illegal path: $(call try-validate-path-is-subdir,$(1))) ) endef ########################################################### ## Given a space-delimited list of filepaths, returns ## nonempty if any cannot be validated to be contained in ## the current directory ## ## $(1): path list to validate define try-validate-paths-are-subdirs $(strip \ $(foreach my_path,$(1),\ $(call try-validate-path-is-subdir,$(my_path))\ ) ) endef define validate-paths-are-subdirs $(if $(call try-validate-paths-are-subdirs,$(1)), $(call pretty-error,Illegal paths:\'$(call try-validate-paths-are-subdirs,$(1))\') ) endef ########################################################### ## Tests of try-validate-path-is-subdir ## and try-validate-paths-are-subdirs define test-validate-paths-are-subdirs $(eval my_error := $(call try-validate-path-is-subdir,/tmp)) \ $(if $(call streq,$(my_error),/tmp starts with a slash), , $(error incorrect error message for path /tmp. Got '$(my_error)') ) \ $(eval my_error := $(call try-validate-path-is-subdir,../sibling)) \ $(if $(call streq,$(my_error),../sibling escapes its parent using '..'), , $(error incorrect error message for path ../sibling. Got '$(my_error)') ) \ $(eval my_error := $(call try-validate-path-is-subdir,child/../../sibling)) \ $(if $(call streq,$(my_error),child/../../sibling escapes its parent using '..'), , $(error incorrect error message for path child/../../sibling. Got '$(my_error)') ) \ $(eval my_error := $(call try-validate-path-is-subdir,)) \ $(if $(call streq,$(my_error),'' is empty), , $(error incorrect error message for empty path ''. Got '$(my_error)') ) \ $(eval my_error := $(call try-validate-path-is-subdir,subdir/subsubdir)) \ $(if $(call streq,$(my_error),), , $(error rejected valid path 'subdir/subsubdir'. Got '$(my_error)') ) $(eval my_error := $(call try-validate-paths-are-subdirs,a/b /c/d e/f)) $(if $(call streq,$(my_error),/c/d starts with a slash), , $(error incorrect error message for path list 'a/b /c/d e/f'. Got '$(my_error)') ) $(eval my_error := $(call try-validate-paths-are-subdirs,a/b c/d)) $(if $(call streq,$(my_error),), , $(error rejected valid path list 'a/b c/d'. Got '$(my_error)') ) endef # run test $(strip $(call test-validate-paths-are-subdirs)) ########################################################### ## Validate jacoco class filters and convert them to ## file arguments ## Jacoco class filters are comma-separated lists of class ## files (android.app.Application), and may have '*' as the ## last character to match all classes in a package ## including subpackages. define jacoco-class-filter-to-file-args $(strip $(call jacoco-validate-file-args,\ $(subst $(comma),$(space),\ $(subst .,/,\ $(strip $(1)))))) endef define jacoco-validate-file-args $(strip $(1)\ $(call validate-paths-are-subdirs,$(1)) $(foreach arg,$(1),\ $(if $(findstring ?,$(arg)),$(call pretty-error,\ '?' filters are not supported in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\ $(if $(findstring *,$(patsubst %*,%,$(arg))),$(call pretty-error,\ '*' is only supported at the end of a filter in LOCAL_JACK_COVERAGE_INCLUDE_FILTER or LOCAL_JACK_COVERAGE_EXCLUDE_FILTER))\ )) endef ########################################################### ## Other includes ########################################################### # Include any vendor specific definitions.mk file -include $(TOPDIR)vendor/*/build/core/definitions.mk -include $(TOPDIR)device/*/build/core/definitions.mk -include $(TOPDIR)product/*/build/core/definitions.mk # Also the project-specific definitions.mk file -include $(TOPDIR)vendor/*/*/build/core/definitions.mk -include $(TOPDIR)device/*/*/build/core/definitions.mk -include $(TOPDIR)product/*/*/build/core/definitions.mk # broken: # $(foreach file,$^,$(if $(findstring,.a,$(suffix $file)),-l$(file),$(file))) ########################################################### ## Misc notes ########################################################### #DEPDIR = .deps #df = $(DEPDIR)/$(*F) #SRCS = foo.c bar.c ... #%.o : %.c # @$(MAKEDEPEND); \ # cp $(df).d $(df).P; \ # sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ # -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \ # rm -f $(df).d # $(COMPILE.c) -o $@ $< #-include $(SRCS:%.c=$(DEPDIR)/%.P) #%.o : %.c # $(COMPILE.c) -MD -o $@ $< # @cp $*.d $*.P; \ # sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ # -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \ # rm -f $*.d ########################################################### # Append the information to generate a RRO package for the # source module. # # $(1): Source module name. # $(2): Whether $(3) is a manifest package name or not. # $(3): Manifest package name if $(2) is true. # Otherwise, android manifest file path of the # source module. # $(4): Whether LOCAL_EXPORT_PACKAGE_RESOURCES is set or # not for the source module. # $(5): Resource overlay list. # $(6): Target partition ########################################################### define append_enforce_rro_sources $(eval ENFORCE_RRO_SOURCES += \ $(strip $(1))||$(strip $(2))||$(strip $(3))||$(strip $(4))||$(call normalize-path-list, $(strip $(5)))||$(strip $(6)) \ ) endef ########################################################### # Generate all RRO packages for source modules stored in # ENFORCE_RRO_SOURCES ########################################################### define generate_all_enforce_rro_packages $(foreach source,$(ENFORCE_RRO_SOURCES), \ $(eval _o := $(subst ||,$(space),$(source))) \ $(eval enforce_rro_source_module := $(word 1,$(_o))) \ $(eval enforce_rro_source_is_manifest_package_name := $(word 2,$(_o))) \ $(eval enforce_rro_source_manifest_package_info := $(word 3,$(_o))) \ $(eval enforce_rro_use_res_lib := $(word 4,$(_o))) \ $(eval enforce_rro_source_overlays := $(subst :, ,$(word 5,$(_o)))) \ $(eval enforce_rro_partition := $(word 6,$(_o))) \ $(eval include $(BUILD_SYSTEM)/generate_enforce_rro.mk) \ $(eval ALL_MODULES.$$(enforce_rro_source_module).REQUIRED_FROM_TARGET += $$(LOCAL_PACKAGE_NAME)) \ ) endef ########################################################### ## Find system_$(VER) in LOCAL_SDK_VERSION ## note: system_server_* is excluded. It's a different API surface ## ## $(1): LOCAL_SDK_VERSION ########################################################### define has-system-sdk-version $(filter-out system_server_%,$(filter system_%,$(1))) endef ########################################################### ## Get numerical version in LOCAL_SDK_VERSION ## ## $(1): LOCAL_SDK_VERSION ########################################################### define get-numeric-sdk-version $(filter-out current,\ $(if $(call has-system-sdk-version,$(1)),$(patsubst system_%,%,$(1)),$(1))) endef ########################################################### ## Verify module name meets character requirements: ## a-z A-Z 0-9 ## _.+-,@~ ## ## This is a subset of bazel's target name restrictions: ## https://docs.bazel.build/versions/master/build-ref.html#name ## ## Kati has problems with '=': https://github.com/google/kati/issues/138 ########################################################### define verify-module-name $(if $(filter-out $(LOCAL_MODULE),$(subst /,,$(LOCAL_MODULE))), \ $(call pretty-warning,Module name contains a /$(comma) use LOCAL_MODULE_STEM and LOCAL_MODULE_RELATIVE_PATH instead)) \ $(if $(call _invalid-name-chars,$(LOCAL_MODULE)), \ $(call pretty-error,Invalid characters in module name: $(call _invalid-name-chars,$(LOCAL_MODULE)))) endef define _invalid-name-chars $(subst _,,$(subst .,,$(subst +,,$(subst -,,$(subst $(comma),,$(subst @,,$(subst ~,,$(subst 0,,$(subst 1,,$(subst 2,,$(subst 3,,$(subst 4,,$(subst 5,,$(subst 6,,$(subst 7,,$(subst 8,,$(subst 9,,$(subst a,,$(subst b,,$(subst c,,$(subst d,,$(subst e,,$(subst f,,$(subst g,,$(subst h,,$(subst i,,$(subst j,,$(subst k,,$(subst l,,$(subst m,,$(subst n,,$(subst o,,$(subst p,,$(subst q,,$(subst r,,$(subst s,,$(subst t,,$(subst u,,$(subst v,,$(subst w,,$(subst x,,$(subst y,,$(subst z,,$(call to-lower,$(1))))))))))))))))))))))))))))))))))))))))))))) endef .KATI_READONLY := verify-module-name _invalid-name-chars ########################################################### ## Verify module stem meets character requirements: ## a-z A-Z 0-9 ## _.+-,@~ ## ## This is a subset of bazel's target name restrictions: ## https://docs.bazel.build/versions/master/build-ref.html#name ## ## $(1): The module stem variable to check ########################################################### define verify-module-stem $(if $(filter-out $($(1)),$(subst /,,$($(1)))), \ $(call pretty-warning,Module stem \($(1)\) contains a /$(comma) use LOCAL_MODULE_RELATIVE_PATH instead)) \ $(if $(call _invalid-name-chars,$($(1))), \ $(call pretty-error,Invalid characters in module stem \($(1)\): $(call _invalid-name-chars,$($(1))))) endef .KATI_READONLY := verify-module-stem $(KATI_obsolete_var \ create-empty-package \ initialize-package-file \ add-jni-shared-libs-to-package \ inherit-package,\ These functions have been removed) ########################################################### ## Verify the variants of a VNDK library are identical ## ## $(1): Path to the core variant shared library file. ## $(2): Path to the vendor variant shared library file. ## $(3): TOOLS_PREFIX ########################################################### LIBRARY_IDENTITY_CHECK_SCRIPT := build/make/tools/check_identical_lib.sh define verify-vndk-libs-identical @echo "Checking VNDK vendor variant: $(2)" $(hide) CLANG_BIN="$(LLVM_PREBUILTS_PATH)" \ CROSS_COMPILE="$(strip $(3))" \ XZ="$(XZ)" \ $(LIBRARY_IDENTITY_CHECK_SCRIPT) $(SOONG_STRIP_PATH) $(1) $(2) endef # Convert Soong libraries that have SDK variant define use_soong_sdk_libraries $(foreach l,$(1),$(if $(filter $(l),$(SOONG_SDK_VARIANT_MODULES)),\ $(l).sdk,$(l))) endef ================================================ FILE: core/deprecation.mk ================================================ # These module types can still be used without warnings or errors. AVAILABLE_BUILD_MODULE_TYPES :=$= \ BUILD_EXECUTABLE \ BUILD_FUZZ_TEST \ BUILD_HEADER_LIBRARY \ BUILD_HOST_JAVA_LIBRARY \ BUILD_HOST_PREBUILT \ BUILD_JAVA_LIBRARY \ BUILD_MULTI_PREBUILT \ BUILD_NATIVE_TEST \ BUILD_NOTICE_FILE \ BUILD_PACKAGE \ BUILD_PHONY_PACKAGE \ BUILD_PREBUILT \ BUILD_RRO_PACKAGE \ BUILD_SHARED_LIBRARY \ BUILD_STATIC_JAVA_LIBRARY \ BUILD_STATIC_LIBRARY \ # These are BUILD_* variables that will throw a warning when used. This is # generally a temporary state until all the devices are marked with the # relevant BUILD_BROKEN_USES_BUILD_* variables, then these would move to # DEFAULT_ERROR_BUILD_MODULE_TYPES. DEFAULT_WARNING_BUILD_MODULE_TYPES :=$= \ # These are BUILD_* variables that are errors to reference, but you can set # BUILD_BROKEN_USES_BUILD_* in your BoardConfig.mk in order to turn them back # to warnings. DEFAULT_ERROR_BUILD_MODULE_TYPES :=$= \ BUILD_COPY_HEADERS \ BUILD_HOST_EXECUTABLE \ BUILD_HOST_SHARED_LIBRARY \ BUILD_HOST_STATIC_LIBRARY \ # These are BUILD_* variables that are always errors to reference. # Setting the BUILD_BROKEN_USES_BUILD_* variables is also an error. OBSOLETE_BUILD_MODULE_TYPES :=$= \ BUILD_AUX_EXECUTABLE \ BUILD_AUX_STATIC_LIBRARY \ BUILD_HOST_DALVIK_JAVA_LIBRARY \ BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY \ BUILD_HOST_FUZZ_TEST \ BUILD_HOST_NATIVE_TEST \ BUILD_HOST_SHARED_TEST_LIBRARY \ BUILD_HOST_STATIC_TEST_LIBRARY \ BUILD_HOST_TEST_CONFIG \ BUILD_NATIVE_BENCHMARK \ BUILD_SHARED_TEST_LIBRARY \ BUILD_STATIC_TEST_LIBRARY \ BUILD_TARGET_TEST_CONFIG \ $(foreach m,$(OBSOLETE_BUILD_MODULE_TYPES),\ $(KATI_obsolete_var $(m),Please convert to Soong) \ $(KATI_obsolete_var BUILD_BROKEN_USES_$(m),Please convert to Soong)) ================================================ FILE: core/dex_preopt.mk ================================================ #################################### # dexpreopt support - typically used on user builds to run dexopt (for Dalvik) or dex2oat (for ART) ahead of time # #################################### include $(BUILD_SYSTEM)/dex_preopt_config.mk # Method returning whether the install path $(1) should be for system_other. # Under SANITIZE_LITE, we do not want system_other. Just put things under /data/asan. ifeq ($(SANITIZE_LITE),true) install-on-system-other = else install-on-system-other = $(filter-out $(PRODUCT_DEXPREOPT_SPEED_APPS) $(PRODUCT_SYSTEM_SERVER_APPS),$(basename $(notdir $(filter $(foreach f,$(SYSTEM_OTHER_ODEX_FILTER),$(TARGET_OUT)/$(f)),$(1))))) endif ifeq ($(WITH_DEXPREOPT), true) ifneq ($(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY), true) ifeq ($(PRODUCT_USES_DEFAULT_ART_CONFIG), true) # Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline extension). # Soong creates a set of variables for Make, one or each boot image. The only reason why the ART # image is exposed to Make is testing (art gtests) and benchmarking (art golem benchmarks). Install # rules that use those variables are in dex_preopt_libart.mk. Here for dexpreopt purposes the infix # is always 'boot' or 'mainline'. DEXPREOPT_INFIX := $(if $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP)),mainline,boot) endif #PRODUCT_USES_DEFAULT_ART_CONFIG endif #WITH_DEXPREOPT_ART_BOOT_IMG_ONLY endif #WITH_DEXPREOPT ================================================ FILE: core/dex_preopt_config.mk ================================================ DEX_PREOPT_CONFIG := $(SOONG_OUT_DIR)/dexpreopt${COVERAGE_SUFFIX}.config ENABLE_PREOPT := true ENABLE_PREOPT_BOOT_IMAGES := true ifneq (true,$(filter true,$(WITH_DEXPREOPT))) # Disable dexpreopt for libraries/apps and for boot images. ENABLE_PREOPT := ENABLE_PREOPT_BOOT_IMAGES := else ifneq (true,$(filter true,$(PRODUCT_USES_DEFAULT_ART_CONFIG))) # Disable dexpreopt for libraries/apps and for boot images: not having default # ART config means that some important system properties are not set, which # would result in passing bad arguments to dex2oat and failing the build. ENABLE_PREOPT := ENABLE_PREOPT_BOOT_IMAGES := else ifeq (true,$(DISABLE_PREOPT)) # Disable dexpreopt for libraries/apps, but may compile boot images. ENABLE_PREOPT := endif ifeq (true,$(DISABLE_PREOPT_BOOT_IMAGES)) # Disable dexpreopt for boot images, but may compile libraries/apps. ENABLE_PREOPT_BOOT_IMAGES := endif endif # The default value for LOCAL_DEX_PREOPT DEX_PREOPT_DEFAULT ?= $(ENABLE_PREOPT) # Whether to fail immediately if verify_uses_libraries check fails, or to keep # going and restrict dexpreopt to not compile any code for the failed module. # # The intended use case for this flag is to have a smoother migration path for # the Java modules that need to add information in their build # files. The flag allows to quickly silence build errors. This flag should be # used with caution and only as a temporary measure, as it masks real errors # and affects performance. ifndef RELAX_USES_LIBRARY_CHECK RELAX_USES_LIBRARY_CHECK := $(if \ $(filter true,$(PRODUCT_BROKEN_VERIFY_USES_LIBRARIES)),true,false) else # Let the environment variable override PRODUCT_BROKEN_VERIFY_USES_LIBRARIES. endif .KATI_READONLY := RELAX_USES_LIBRARY_CHECK # The default filter for which files go into the system_other image (if it is # being used). Note that each pattern p here matches both '/

' and /system/

'. # To bundle everything one should set this to '%'. SYSTEM_OTHER_ODEX_FILTER ?= \ app/% \ priv-app/% \ system_ext/app/% \ system_ext/priv-app/% \ product/app/% \ product/priv-app/% \ # Global switch to control if updatable boot jars are included in dexpreopt. DEX_PREOPT_WITH_UPDATABLE_BCP := true # Conditional to building on linux, as dex2oat currently does not work on darwin. ifeq ($(HOST_OS),linux) # Add mini-debug-info to the boot classpath unless explicitly asked not to. ifneq (false,$(WITH_DEXPREOPT_DEBUG_INFO)) PRODUCT_DEX_PREOPT_BOOT_FLAGS += --generate-mini-debug-info endif endif # Get value of a property. It is first searched from PRODUCT_VENDOR_PROPERTIES # and then falls back to PRODUCT_SYSTEM_PROPERTIES # $1: name of the property define get-product-default-property $(strip \ $(eval _prop := $(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_VENDOR_PROPERTIES))))\ $(if $(_prop),$(_prop),$(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_SYSTEM_PROPERTIES))))) endef DEX2OAT_IMAGE_XMS := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xms) DEX2OAT_IMAGE_XMX := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xmx) DEX2OAT_XMS := $(call get-product-default-property,dalvik.vm.dex2oat-Xms) DEX2OAT_XMX := $(call get-product-default-property,dalvik.vm.dex2oat-Xmx) ifeq ($(WRITE_SOONG_VARIABLES),true) $(call json_start) $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT))) $(call add_json_bool, DisablePreoptBootImages, $(call invert_bool,$(ENABLE_PREOPT_BOOT_IMAGES))) $(call add_json_list, DisablePreoptModules, $(DEXPREOPT_DISABLED_MODULES)) $(call add_json_bool, OnlyPreoptArtBootImage , $(filter true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY))) $(call add_json_bool, PreoptWithUpdatableBcp, $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP))) $(call add_json_bool, DontUncompressPrivAppsDex, $(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))) $(call add_json_list, ModulesLoadedByPrivilegedModules, $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES)) $(call add_json_bool, HasSystemOther, $(BOARD_USES_SYSTEM_OTHER_ODEX)) $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER)) $(call add_json_bool, DisableGenerateProfile, $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE))) $(call add_json_str, ProfileDir, $(PRODUCT_DEX_PREOPT_PROFILE_DIR)) $(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS)) $(call add_json_list, ApexBootJars, $(filter-out $(APEX_BOOT_JARS_EXCLUDED), $(PRODUCT_APEX_BOOT_JARS))) $(call add_json_list, ArtApexJars, $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS))) $(call add_json_list, TestOnlyArtBootImageJars, $(PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS)) $(call add_json_list, SystemServerJars, $(PRODUCT_SYSTEM_SERVER_JARS)) $(call add_json_list, SystemServerApps, $(PRODUCT_SYSTEM_SERVER_APPS)) $(call add_json_list, ApexSystemServerJars, $(PRODUCT_APEX_SYSTEM_SERVER_JARS)) $(call add_json_list, StandaloneSystemServerJars, $(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS)) $(call add_json_list, ApexStandaloneSystemServerJars, $(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS)) $(call add_json_bool, BrokenSuboptimalOrderOfSystemServerJars, $(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS)) $(call add_json_list, SpeedApps, $(PRODUCT_DEXPREOPT_SPEED_APPS)) $(call add_json_list, PreoptFlags, $(PRODUCT_DEX_PREOPT_DEFAULT_FLAGS)) $(call add_json_str, DefaultCompilerFilter, $(PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER)) $(call add_json_str, SystemServerCompilerFilter, $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)) $(call add_json_bool, GenerateDmFiles, $(PRODUCT_DEX_PREOPT_GENERATE_DM_FILES)) $(call add_json_bool, NeverAllowStripping, $(PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING)) $(call add_json_bool, NoDebugInfo, $(filter false,$(WITH_DEXPREOPT_DEBUG_INFO))) $(call add_json_bool, DontResolveStartupStrings, $(filter false,$(PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS))) $(call add_json_bool, AlwaysSystemServerDebugInfo, $(filter true,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO))) $(call add_json_bool, NeverSystemServerDebugInfo, $(filter false,$(PRODUCT_SYSTEM_SERVER_DEBUG_INFO))) $(call add_json_bool, AlwaysOtherDebugInfo, $(filter true,$(PRODUCT_OTHER_JAVA_DEBUG_INFO))) $(call add_json_bool, NeverOtherDebugInfo, $(filter false,$(PRODUCT_OTHER_JAVA_DEBUG_INFO))) $(call add_json_bool, IsEng, $(filter eng,$(TARGET_BUILD_VARIANT))) $(call add_json_bool, SanitizeLite, $(SANITIZE_LITE)) $(call add_json_bool, DefaultAppImages, $(WITH_DEX_PREOPT_APP_IMAGE)) $(call add_json_bool, RelaxUsesLibraryCheck, $(filter true,$(RELAX_USES_LIBRARY_CHECK))) $(call add_json_str, Dex2oatXmx, $(DEX2OAT_XMX)) $(call add_json_str, Dex2oatXms, $(DEX2OAT_XMS)) $(call add_json_str, EmptyDirectory, $(OUT_DIR)/empty) $(call add_json_str, EnableUffdGc, $(ENABLE_UFFD_GC)) ifdef TARGET_ARCH $(call add_json_map, CpuVariant) $(call add_json_str, $(TARGET_ARCH), $(DEX2OAT_TARGET_CPU_VARIANT)) ifdef TARGET_2ND_ARCH $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT)) endif $(call end_json_map) $(call add_json_map, InstructionSetFeatures) $(call add_json_str, $(TARGET_ARCH), $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) ifdef TARGET_2ND_ARCH $(call add_json_str, $(TARGET_2ND_ARCH), $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) endif $(call end_json_map) endif $(call add_json_list, BootImageProfiles, $(PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION)) $(call add_json_str, BootFlags, $(PRODUCT_DEX_PREOPT_BOOT_FLAGS)) $(call add_json_str, Dex2oatImageXmx, $(DEX2OAT_IMAGE_XMX)) $(call add_json_str, Dex2oatImageXms, $(DEX2OAT_IMAGE_XMS)) $(call json_end) $(shell mkdir -p $(dir $(DEX_PREOPT_CONFIG))) $(file >$(DEX_PREOPT_CONFIG).tmp,$(json_contents)) $(shell \ if ! cmp -s $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); then \ mv $(DEX_PREOPT_CONFIG).tmp $(DEX_PREOPT_CONFIG); \ else \ rm $(DEX_PREOPT_CONFIG).tmp; \ fi) endif ================================================ FILE: core/dex_preopt_config_merger.py ================================================ #!/usr/bin/env python # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """ A tool for merging dexpreopt.config files for dependencies into the dexpreopt.config file of the library/app that uses them. This is needed to generate class loader context (CLC) for dexpreopt. In Make there is no topological order when processing different modules, so a dependency module may have not been processed yet by the time the dependent module is processed. Therefore makefiles communicate the information from dependencies via dexpreopt.config files and add file-level dependencies from a module dexpreopt.config to its dependency configs. The actual patching of configs is done by this script, which is called from the makefiles. """ from __future__ import print_function import json from collections import OrderedDict import sys def main(): """Program entry point.""" if len(sys.argv) < 2: raise SystemExit('usage: %s [dep-config ...]' % sys.argv[0]) # Read all JSON configs. cfgs = [] for arg in sys.argv[1:]: with open(arg, 'r') as f: cfgs.append(json.load(f, object_pairs_hook=OrderedDict)) # The first config is the dexpreopted library/app, the rest are its # dependencies. cfg0 = cfgs[0] # Put dependency configs in a map keyed on module name (for easier lookup). uses_libs = {} for cfg in cfgs[1:]: uses_libs[cfg['Name']] = cfg # Load the original CLC map. clc_map = cfg0['ClassLoaderContexts'] # Create a new CLC map that will be a copy of the original one with patched # fields from dependency dexpreopt.config files. clc_map2 = OrderedDict() # Patch CLC for each SDK version. Although this should not be necessary for # compatibility libraries (so-called "conditional CLC"), because they all have # known names, known paths in system/framework, and no subcontext. But keep # the loop in case this changes in the future. for sdk_ver in clc_map: clcs = clc_map[sdk_ver] clcs2 = [] for clc in clcs: lib = clc['Name'] if lib in uses_libs: ulib = uses_libs[lib] # The real name (may be different from the module name). clc['Name'] = ulib['ProvidesUsesLibrary'] # On-device (install) path to the dependency DEX jar file. clc['Device'] = ulib['DexLocation'] # CLC of the dependency becomes a subcontext. We only need sub-CLC for # 'any' version because all other versions are for compatibility # libraries, which exist only for apps and not for libraries. clc['Subcontexts'] = ulib['ClassLoaderContexts'].get('any') else: # dexpreopt.config for this is not among the script # arguments, which may be the case with compatibility libraries that # don't need patching anyway. Just use the original CLC. pass clcs2.append(clc) clc_map2[sdk_ver] = clcs2 # Overwrite the original class loader context with the patched one. cfg0['ClassLoaderContexts'] = clc_map2 # Update dexpreopt.config file. with open(sys.argv[1], 'w') as f: f.write(json.dumps(cfgs[0], indent=4, separators=(',', ': '))) if __name__ == '__main__': main() ================================================ FILE: core/dex_preopt_odex_install.mk ================================================ # dexpreopt_odex_install.mk is used to define odex creation rules for JARs and APKs # This file depends on variables set in base_rules.mk # Input variables: my_manifest_or_apk # Output variables: LOCAL_DEX_PREOPT, LOCAL_UNCOMPRESS_DEX ifeq (true,$(LOCAL_USE_EMBEDDED_DEX)) LOCAL_UNCOMPRESS_DEX := true else LOCAL_UNCOMPRESS_DEX := endif # We explicitly uncompress APKs of privileged apps, and used by # privileged apps ifneq (true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)) ifeq (true,$(LOCAL_PRIVILEGED_MODULE)) LOCAL_UNCOMPRESS_DEX := true endif ifneq (,$(filter $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES), $(LOCAL_MODULE))) LOCAL_UNCOMPRESS_DEX := true endif endif # DONT_UNCOMPRESS_PRIV_APPS_DEXS # Setting LOCAL_DEX_PREOPT based on WITH_DEXPREOPT, LOCAL_DEX_PREOPT, etc LOCAL_DEX_PREOPT := $(strip $(LOCAL_DEX_PREOPT)) ifndef LOCAL_DEX_PREOPT # LOCAL_DEX_PREOPT undefined LOCAL_DEX_PREOPT := $(DEX_PREOPT_DEFAULT) endif ifeq (false,$(LOCAL_DEX_PREOPT)) LOCAL_DEX_PREOPT := endif # Disable preopt for tests. ifneq (,$(filter $(LOCAL_MODULE_TAGS),tests)) LOCAL_DEX_PREOPT := endif # If we have product-specific config for this module? ifneq (,$(filter $(LOCAL_MODULE),$(DEXPREOPT_DISABLED_MODULES))) LOCAL_DEX_PREOPT := endif # Disable preopt for DISABLE_PREOPT ifeq (true,$(DISABLE_PREOPT)) LOCAL_DEX_PREOPT := endif # Disable preopt if not WITH_DEXPREOPT ifneq (true,$(WITH_DEXPREOPT)) LOCAL_DEX_PREOPT := endif ifdef LOCAL_UNINSTALLABLE_MODULE LOCAL_DEX_PREOPT := endif # Disable preopt if the app contains no java code. ifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR))) LOCAL_DEX_PREOPT := endif ifeq (true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY)) LOCAL_DEX_PREOPT := endif my_process_profile := my_profile_is_text_listing := ifeq (false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)) LOCAL_DEX_PREOPT_GENERATE_PROFILE := false endif ifndef LOCAL_DEX_PREOPT_GENERATE_PROFILE # If LOCAL_DEX_PREOPT_GENERATE_PROFILE is not defined, default it based on the existence of the # profile class listing. TODO: Use product specific directory here. ifdef PRODUCT_DEX_PREOPT_PROFILE_DIR LOCAL_DEX_PREOPT_PROFILE := $(PRODUCT_DEX_PREOPT_PROFILE_DIR)/$(LOCAL_MODULE).prof ifneq (,$(wildcard $(LOCAL_DEX_PREOPT_PROFILE))) my_process_profile := true my_profile_is_text_listing := endif endif else my_process_profile := $(LOCAL_DEX_PREOPT_GENERATE_PROFILE) my_profile_is_text_listing := true LOCAL_DEX_PREOPT_PROFILE := $(LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING) endif ifeq (true,$(my_process_profile)) ifndef LOCAL_DEX_PREOPT_PROFILE $(call pretty-error,Must have specified class listing (LOCAL_DEX_PREOPT_PROFILE)) endif ifeq (,$(dex_preopt_profile_src_file)) $(call pretty-error, Internal error: dex_preopt_profile_src_file must be set) endif endif ################################################################################ # Local module variables and functions used in dexpreopt and manifest_check. ################################################################################ # TODO(b/132357300): This may filter out too much, as PRODUCT_PACKAGES doesn't # include all packages (the full list is unknown until reading all Android.mk # makefiles). As a consequence, a library may be present but not included in # dexpreopt, which will result in class loader context mismatch and a failure # to load dexpreopt code on device. # However, we have to do filtering here. Otherwise, we may include extra # libraries that Soong and Make don't generate build rules for (e.g., a library # that exists in the source tree but not installable), and therefore get Ninja # errors. # We have deferred CLC computation to the Ninja phase, but the dependency # computation still needs to be done early. For now, this is the best we can do. my_filtered_optional_uses_libraries := $(filter $(PRODUCT_PACKAGES), \ $(LOCAL_OPTIONAL_USES_LIBRARIES)) ifeq ($(LOCAL_MODULE_CLASS),APPS) # compatibility libraries are added to class loader context of an app only if # targetSdkVersion in the app's manifest is lower than the given SDK version my_dexpreopt_libs_compat_28 := \ org.apache.http.legacy my_dexpreopt_libs_compat_29 := \ android.hidl.manager-V1.0-java \ android.hidl.base-V1.0-java my_dexpreopt_libs_compat_30 := \ android.test.base \ android.test.mock my_dexpreopt_libs_compat := \ $(my_dexpreopt_libs_compat_28) \ $(my_dexpreopt_libs_compat_29) \ $(my_dexpreopt_libs_compat_30) else my_dexpreopt_libs_compat := endif my_dexpreopt_libs := \ $(LOCAL_USES_LIBRARIES) \ $(my_filtered_optional_uses_libraries) # The order needs to be deterministic. my_dexpreopt_libs_all := $(sort $(my_dexpreopt_libs) $(my_dexpreopt_libs_compat)) # Module dexpreopt.config depends on dexpreopt.config files of each # dependency, because these libraries may be processed after # the current module by Make (there's no topological order), so the dependency # information (paths, class loader context) may not be ready yet by the time # this dexpreopt.config is generated. So it's necessary to add file-level # dependencies between dexpreopt.config files. my_dexpreopt_dep_configs := $(foreach lib, \ $(filter-out $(my_dexpreopt_libs_compat) $(FRAMEWORK_LIBRARIES),$(LOCAL_USES_LIBRARIES) $(my_filtered_optional_uses_libraries)), \ $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,)/dexpreopt.config) # 1: SDK version # 2: list of libraries # # Make does not process modules in topological order wrt. # dependencies, therefore we cannot rely on variables to get the information # about dependencies (in particular, their on-device path and class loader # context). This information is communicated via dexpreopt.config files: each # config depends on configs for dependencies of this module, # and the dex_preopt_config_merger.py script reads all configs and inserts the # missing bits from dependency configs into the module config. # # By default on-device path is /system/framework/*.jar, and class loader # subcontext is empty. These values are correct for compatibility libraries, # which are special and not handled by dex_preopt_config_merger.py. # add_json_class_loader_context = \ $(call add_json_array, $(1)) \ $(foreach lib, $(2),\ $(call add_json_map_anon) \ $(call add_json_str, Name, $(lib)) \ $(call add_json_str, Host, $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \ $(call add_json_str, Device, /system/framework/$(lib).jar) \ $(call add_json_val, Subcontexts, null) \ $(call end_json_map)) \ $(call end_json_array) ################################################################################ # Verify coherence between the build system and the manifest. ################################################################################ # Some libraries do not have a manifest, so there is nothing to check against. # Handle it as if the manifest had zero tags: it is ok unless the # module has non-empty LOCAL_USES_LIBRARIES or LOCAL_OPTIONAL_USES_LIBRARIES. ifndef my_manifest_or_apk ifneq (,$(strip $(LOCAL_USES_LIBRARIES)$(LOCAL_OPTIONAL_USES_LIBRARIES))) $(error $(LOCAL_MODULE) has non-empty list but no manifest) else LOCAL_ENFORCE_USES_LIBRARIES := false endif endif # Disable the check for tests. ifneq (,$(filter $(LOCAL_MODULE_TAGS),tests)) LOCAL_ENFORCE_USES_LIBRARIES := false endif ifneq (,$(LOCAL_COMPATIBILITY_SUITE)) LOCAL_ENFORCE_USES_LIBRARIES := false # Enable the check for WTS ifneq ($(filter wts,$(LOCAL_COMPATIBILITY_SUITE)),) LOCAL_ENFORCE_USES_LIBRARIES := true endif endif # Disable the check if the app contains no java code. ifeq (,$(strip $(built_dex)$(my_prebuilt_src_file)$(LOCAL_SOONG_DEX_JAR))) LOCAL_ENFORCE_USES_LIBRARIES := false endif # Disable checks if dexpreopt is globally disabled. # Without dexpreopt the check is not necessary, and although it is good to have, # it is difficult to maintain on non-linux build platforms where dexpreopt is # generally disabled (the check may fail due to various unrelated reasons, such # as a failure to get manifest from an APK). ifneq (true,$(WITH_DEXPREOPT)) LOCAL_ENFORCE_USES_LIBRARIES := false else ifeq (true,$(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY)) LOCAL_ENFORCE_USES_LIBRARIES := false endif # Verify LOCAL_USES_LIBRARIES/LOCAL_OPTIONAL_USES_LIBRARIES against the manifest. ifndef LOCAL_ENFORCE_USES_LIBRARIES LOCAL_ENFORCE_USES_LIBRARIES := true endif my_enforced_uses_libraries := ifeq (true,$(LOCAL_ENFORCE_USES_LIBRARIES)) my_verify_script := build/soong/scripts/manifest_check.py my_uses_libs_args := $(patsubst %,--uses-library %,$(LOCAL_USES_LIBRARIES)) my_optional_uses_libs_args := $(patsubst %,--optional-uses-library %, \ $(LOCAL_OPTIONAL_USES_LIBRARIES)) my_relax_check_arg := $(if $(filter true,$(RELAX_USES_LIBRARY_CHECK)), \ --enforce-uses-libraries-relax,) my_dexpreopt_config_args := $(patsubst %,--dexpreopt-config %,$(my_dexpreopt_dep_configs)) my_enforced_uses_libraries := $(intermediates)/enforce_uses_libraries.status $(my_enforced_uses_libraries): PRIVATE_USES_LIBRARIES := $(my_uses_libs_args) $(my_enforced_uses_libraries): PRIVATE_OPTIONAL_USES_LIBRARIES := $(my_optional_uses_libs_args) $(my_enforced_uses_libraries): PRIVATE_DEXPREOPT_CONFIGS := $(my_dexpreopt_config_args) $(my_enforced_uses_libraries): PRIVATE_RELAX_CHECK := $(my_relax_check_arg) $(my_enforced_uses_libraries): $(AAPT2) $(my_enforced_uses_libraries): $(my_verify_script) $(my_enforced_uses_libraries): $(my_dexpreopt_dep_configs) $(my_enforced_uses_libraries): $(my_manifest_or_apk) @echo Verifying uses-libraries: $< rm -f $@ $(my_verify_script) \ --enforce-uses-libraries \ --enforce-uses-libraries-status $@ \ --aapt $(AAPT2) \ $(PRIVATE_USES_LIBRARIES) \ $(PRIVATE_OPTIONAL_USES_LIBRARIES) \ $(PRIVATE_DEXPREOPT_CONFIGS) \ $(PRIVATE_RELAX_CHECK) \ $< $(LOCAL_BUILT_MODULE) : $(my_enforced_uses_libraries) endif ################################################################################ # Dexpreopt command. ################################################################################ my_dexpreopt_archs := my_dexpreopt_images := my_dexpreopt_images_deps := my_dexpreopt_image_locations_on_host := my_dexpreopt_image_locations_on_device := my_dexpreopt_infix := $(DEXPREOPT_INFIX) my_create_dexpreopt_config := ifdef LOCAL_DEX_PREOPT ifeq (,$(filter PRESIGNED,$(LOCAL_CERTIFICATE))) # Store uncompressed dex files preopted in /system ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true) ifeq ($(call install-on-system-other, $(my_module_path)),) LOCAL_UNCOMPRESS_DEX := true endif # install-on-system-other else # BOARD_USES_SYSTEM_OTHER_ODEX LOCAL_UNCOMPRESS_DEX := true endif endif my_create_dexpreopt_config := true endif # dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true, # but dexpreopt config files are required to dexpreopt in post-processing. ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true) my_create_dexpreopt_config := true endif ifeq ($(my_create_dexpreopt_config), true) ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES) my_module_multilib := $(LOCAL_MULTILIB) # If the module is not an SDK library and it's a system server jar, only preopt the primary arch. ifeq (,$(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_MODULE))) # For a Java library, by default we build odex for both 1st arch and 2nd arch. # But it can be overridden with "LOCAL_MULTILIB := first". ifneq (,$(filter $(PRODUCT_SYSTEM_SERVER_JARS),$(LOCAL_MODULE))) # For system server jars, we build for only "first". my_module_multilib := first endif endif # Only preopt primary arch for translated arch since there is only an image there. ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true) my_module_multilib := first endif # ################################################# # Odex for the 1st arch my_dexpreopt_archs += $(TARGET_ARCH) my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_ARCH)) my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_ARCH)) # Odex for the 2nd arch ifdef TARGET_2ND_ARCH ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) ifneq (first,$(my_module_multilib)) my_dexpreopt_archs += $(TARGET_2ND_ARCH) my_dexpreopt_images += $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH)) my_dexpreopt_images_deps += $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_2ND_ARCH)) endif # my_module_multilib is not first. endif # TARGET_TRANSLATE_2ND_ARCH not true endif # TARGET_2ND_ARCH # ################################################# else # must be APPS # The preferred arch # Save the module multilib since setup_one_odex modifies it. my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH) my_dexpreopt_images += \ $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) my_dexpreopt_images_deps += \ $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) ifdef TARGET_2ND_ARCH ifeq ($(my_module_multilib),both) # The non-preferred arch my_2nd_arch_prefix := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),,$(TARGET_2ND_ARCH_VAR_PREFIX)) my_dexpreopt_archs += $(TARGET_$(my_2nd_arch_prefix)ARCH) my_dexpreopt_images += \ $(DEXPREOPT_IMAGE_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) my_dexpreopt_images_deps += \ $(DEXPREOPT_IMAGE_DEPS_$(my_dexpreopt_infix)_$(TARGET_$(my_2nd_arch_prefix)ARCH)) endif # LOCAL_MULTILIB is both endif # TARGET_2ND_ARCH endif # LOCAL_MODULE_CLASS my_dexpreopt_image_locations_on_host += $(DEXPREOPT_IMAGE_LOCATIONS_ON_HOST$(my_dexpreopt_infix)) my_dexpreopt_image_locations_on_device += $(DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE$(my_dexpreopt_infix)) # Record dex-preopt config. DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT := $(LOCAL_DEX_PREOPT) DEXPREOPT.$(LOCAL_MODULE).MULTILIB := $(LOCAL_MULTILIB) DEXPREOPT.$(LOCAL_MODULE).DEX_PREOPT_FLAGS := $(LOCAL_DEX_PREOPT_FLAGS) DEXPREOPT.$(LOCAL_MODULE).PRIVILEGED_MODULE := $(LOCAL_PRIVILEGED_MODULE) DEXPREOPT.$(LOCAL_MODULE).VENDOR_MODULE := $(LOCAL_VENDOR_MODULE) DEXPREOPT.$(LOCAL_MODULE).TARGET_ARCH := $(LOCAL_MODULE_TARGET_ARCH) DEXPREOPT.$(LOCAL_MODULE).INSTALLED_STRIPPED := $(LOCAL_INSTALLED_MODULE) DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS) := $(sort \ $(DEXPREOPT.MODULES.$(LOCAL_MODULE_CLASS)) $(LOCAL_MODULE)) $(call json_start) # DexPath is not set: it will be filled in by dexpreopt_gen. $(call add_json_str, Name, $(LOCAL_MODULE)) $(call add_json_str, DexLocation, $(patsubst $(PRODUCT_OUT)%,%,$(LOCAL_INSTALLED_MODULE))) $(call add_json_str, BuildPath, $(LOCAL_BUILT_MODULE)) $(call add_json_str, ManifestPath, $(full_android_manifest)) $(call add_json_str, ExtrasOutputPath, $$2) $(call add_json_bool, Privileged, $(filter true,$(LOCAL_PRIVILEGED_MODULE))) $(call add_json_bool, UncompressedDex, $(filter true,$(LOCAL_UNCOMPRESS_DEX))) $(call add_json_bool, HasApkLibraries, $(LOCAL_APK_LIBRARIES)) $(call add_json_list, PreoptFlags, $(LOCAL_DEX_PREOPT_FLAGS)) $(call add_json_str, ProfileClassListing, $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))) $(call add_json_bool, ProfileIsTextListing, $(my_profile_is_text_listing)) $(call add_json_str, EnforceUsesLibrariesStatusFile, $(my_enforced_uses_libraries)) $(call add_json_bool, EnforceUsesLibraries, $(filter true,$(LOCAL_ENFORCE_USES_LIBRARIES))) $(call add_json_str, ProvidesUsesLibrary, $(firstword $(LOCAL_PROVIDES_USES_LIBRARY) $(LOCAL_MODULE))) $(call add_json_map, ClassLoaderContexts) $(call add_json_class_loader_context, any, $(my_dexpreopt_libs)) $(call add_json_class_loader_context, 28, $(my_dexpreopt_libs_compat_28)) $(call add_json_class_loader_context, 29, $(my_dexpreopt_libs_compat_29)) $(call add_json_class_loader_context, 30, $(my_dexpreopt_libs_compat_30)) $(call end_json_map) $(call add_json_list, Archs, $(my_dexpreopt_archs)) $(call add_json_list, DexPreoptImages, $(my_dexpreopt_images)) $(call add_json_list, DexPreoptImageLocationsOnHost, $(my_dexpreopt_image_locations_on_host)) $(call add_json_list, DexPreoptImageLocationsOnDevice,$(my_dexpreopt_image_locations_on_device)) $(call add_json_list, PreoptBootClassPathDexFiles, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) $(call add_json_list, PreoptBootClassPathDexLocations,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) $(call add_json_bool, NoCreateAppImage, $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE))) $(call add_json_bool, ForceCreateAppImage, $(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE))) $(call add_json_bool, PresignedPrebuilt, $(filter PRESIGNED,$(LOCAL_CERTIFICATE))) $(call json_end) my_dexpreopt_config := $(intermediates)/dexpreopt.config my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py $(my_dexpreopt_config): $(my_dexpreopt_dep_configs) $(my_dexpreopt_config_merger) $(my_dexpreopt_config): PRIVATE_MODULE := $(LOCAL_MODULE) $(my_dexpreopt_config): PRIVATE_CONTENTS := $(json_contents) $(my_dexpreopt_config): PRIVATE_DEP_CONFIGS := $(my_dexpreopt_dep_configs) $(my_dexpreopt_config): PRIVATE_CONFIG_MERGER := $(my_dexpreopt_config_merger) $(my_dexpreopt_config): @echo "$(PRIVATE_MODULE) dexpreopt.config" echo -e -n '$(subst $(newline),\n,$(subst ','\'',$(subst \,\\,$(PRIVATE_CONTENTS))))' > $@ $(PRIVATE_CONFIG_MERGER) $@ $(PRIVATE_DEP_CONFIGS) $(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing))) $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing) # System server jars defined in Android.mk are deprecated. ifneq (true, $(PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS)) ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS) $(PRODUCT_APEX_SYSTEM_SERVER_JARS))) $(error System server jars defined in Android.mk are deprecated. \ Convert $(LOCAL_MODULE) to Android.bp or temporarily disable the error \ with 'PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS := true') endif endif ifdef LOCAL_DEX_PREOPT # System server jars must be copied into predefined locations expected by # dexpreopt. Copy rule must be exposed to Ninja (as it uses these files as # inputs), so it cannot go in dexpreopt.sh. ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS))) my_dexpreopt_jar_copy := $(OUT_DIR)/soong/system_server_dexjars/$(LOCAL_MODULE).jar $(my_dexpreopt_jar_copy): PRIVATE_BUILT_MODULE := $(LOCAL_BUILT_MODULE) $(my_dexpreopt_jar_copy): $(LOCAL_BUILT_MODULE) @cp $(PRIVATE_BUILT_MODULE) $@ endif # The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list # of all packages that are installed on the device. We use `grep` to filter the list by the app's # dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime # from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns. my_dexpreopt_product_packages := $(intermediates)/product_packages.txt .KATI_RESTAT: $(my_dexpreopt_product_packages) $(my_dexpreopt_product_packages): PRIVATE_MODULE := $(LOCAL_MODULE) $(my_dexpreopt_product_packages): PRIVATE_LIBS := $(my_dexpreopt_libs_all) $(my_dexpreopt_product_packages): PRIVATE_STAGING := $(my_dexpreopt_product_packages).tmp $(my_dexpreopt_product_packages): $(PRODUCT_OUT)/product_packages.txt @echo "$(PRIVATE_MODULE) dexpreopt product_packages" ifneq (,$(my_dexpreopt_libs_all)) grep -F -x \ $(addprefix -e ,$(PRIVATE_LIBS)) \ $(PRODUCT_OUT)/product_packages.txt \ > $(PRIVATE_STAGING) \ || true else rm -f $(PRIVATE_STAGING) && touch $(PRIVATE_STAGING) endif rsync --checksum $(PRIVATE_STAGING) $@ my_dexpreopt_script := $(intermediates)/dexpreopt.sh .KATI_RESTAT: $(my_dexpreopt_script) $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE) $(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE) $(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config) $(my_dexpreopt_script): PRIVATE_PRODUCT_PACKAGES := $(my_dexpreopt_product_packages) $(my_dexpreopt_script): $(DEXPREOPT_GEN) $(my_dexpreopt_script): $(my_dexpreopt_jar_copy) $(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(DEX_PREOPT_CONFIG_FOR_MAKE) $(my_dexpreopt_product_packages) @echo "$(PRIVATE_MODULE) dexpreopt gen" $(DEXPREOPT_GEN) \ -global_soong $(PRIVATE_GLOBAL_SOONG_CONFIG) \ -global $(PRIVATE_GLOBAL_CONFIG) \ -module $(PRIVATE_MODULE_CONFIG) \ -dexpreopt_script $@ \ -out_dir $(OUT_DIR) \ -product_packages $(PRIVATE_PRODUCT_PACKAGES) my_dexpreopt_deps := $(my_dex_jar) my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE)) my_dexpreopt_deps += \ $(foreach lib, $(my_dexpreopt_libs_all), \ $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) my_dexpreopt_deps += $(my_dexpreopt_images_deps) my_dexpreopt_deps += $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES) ifeq ($(LOCAL_ENFORCE_USES_LIBRARIES),true) my_dexpreopt_deps += $(intermediates)/enforce_uses_libraries.status endif # We need to add all the installed files to ALL_MODULES.$(my_register_name).INSTALLED in order # for the build system to properly track installed files. (for sbom, installclean, etc) # We install all the files in a zip file generated at execution time, which means we have to guess # what's going to be in that zip file before it's created. We then check at executation time that # our guess is correct. # _system_other corresponds to OdexOnSystemOtherByName() in soong. # The other paths correspond to dexpreoptCommand() _dexlocation := $(patsubst $(PRODUCT_OUT)/%,%,$(LOCAL_INSTALLED_MODULE)) _dexname := $(basename $(notdir $(_dexlocation))) _system_other := $(strip $(if $(strip $(BOARD_USES_SYSTEM_OTHER_ODEX)), \ $(if $(strip $(SANITIZE_LITE)),, \ $(if $(filter $(_dexname),$(PRODUCT_DEXPREOPT_SPEED_APPS))$(filter $(_dexname),$(PRODUCT_SYSTEM_SERVER_APPS)),, \ $(if $(strip $(foreach myfilter,$(SYSTEM_OTHER_ODEX_FILTER),$(filter system/$(myfilter),$(_dexlocation))$(filter $(myfilter),$(_dexlocation)))), \ system_other/))))) # _dexdir has a trailing / _dexdir := $(_system_other)$(dir $(_dexlocation)) my_dexpreopt_zip_contents := $(sort \ $(foreach arch,$(my_dexpreopt_archs), \ $(_dexdir)oat/$(arch)/$(_dexname).odex \ $(_dexdir)oat/$(arch)/$(_dexname).vdex \ $(if $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE)),, \ $(if $(my_process_profile)$(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE)), \ $(_dexdir)oat/$(arch)/$(_dexname).art))) \ $(if $(my_process_profile),$(_dexlocation).prof)) _dexlocation := _dexdir := _dexname := _system_other := my_dexpreopt_zip := $(intermediates)/dexpreopt.zip $(my_dexpreopt_zip): PRIVATE_MODULE := $(LOCAL_MODULE) $(my_dexpreopt_zip): $(my_dexpreopt_deps) $(my_dexpreopt_zip): | $(DEXPREOPT_GEN_DEPS) $(my_dexpreopt_zip): .KATI_DEPFILE := $(my_dexpreopt_zip).d $(my_dexpreopt_zip): PRIVATE_DEX := $(my_dex_jar) $(my_dexpreopt_zip): PRIVATE_SCRIPT := $(my_dexpreopt_script) $(my_dexpreopt_zip): PRIVATE_ZIP_CONTENTS := $(my_dexpreopt_zip_contents) $(my_dexpreopt_zip): $(my_dexpreopt_script) @echo "$(PRIVATE_MODULE) dexpreopt" rm -f $@ echo -n > $@.contents $(foreach f,$(PRIVATE_ZIP_CONTENTS),echo "$(f)" >> $@.contents$(newline)) bash $(PRIVATE_SCRIPT) $(PRIVATE_DEX) $@ if ! diff <(zipinfo -1 $@ | sort) $@.contents >&2; then \ echo "Contents of $@ did not match what make was expecting." >&2 && exit 1; \ fi $(foreach installed_dex_file,$(my_dexpreopt_zip_contents),\ $(eval $(PRODUCT_OUT)/$(installed_dex_file): $(my_dexpreopt_zip) \ $(newline) unzip -qoDD -d $(PRODUCT_OUT) $(my_dexpreopt_zip) $(installed_dex_file))) ALL_MODULES.$(my_register_name).INSTALLED += $(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents)) # Normally this happens in sbom.mk, which is included from base_rules.mk. But since # dex_preopt_odex_install.mk is included after base_rules.mk, it misses these odex files. $(foreach installed_file,$(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents)), \ $(eval ALL_INSTALLED_FILES.$(installed_file) := $(my_register_name))) my_dexpreopt_config := my_dexpreopt_config_for_postprocessing := my_dexpreopt_jar_copy := my_dexpreopt_product_packages := my_dexpreopt_script := my_dexpreopt_zip := my_dexpreopt_zip_contents := endif # LOCAL_DEX_PREOPT endif # my_create_dexpreopt_config my_dexpreopt_libs_all := ================================================ FILE: core/distdir.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # When specifying "dist", the user has asked that we copy the important # files from this build into DIST_DIR. # list of all goals that depend on any dist files _all_dist_goals := # pairs of goal:distfile _all_dist_goal_output_pairs := # pairs of srcfile:distfile _all_dist_src_dst_pairs := # Other parts of the system should use this function to associate # certain files with certain goals. When those goals are built # and "dist" is specified, the marked files will be copied to DIST_DIR. # # $(1): a list of goals (e.g. droid, sdk, ndk). These must be PHONY # $(2): the dist files to add to those goals. If the file contains ':', # the text following the colon is the name that the file is copied # to under the dist directory. Subdirs are ok, and will be created # at copy time if necessary. define dist-for-goals $(if $(strip $(2)), \ $(eval _all_dist_goals += $$(1))) \ $(foreach file,$(2), \ $(eval src := $(call word-colon,1,$(file))) \ $(eval dst := $(call word-colon,2,$(file))) \ $(if $(dst),,$(eval dst := $$(notdir $$(src)))) \ $(eval _all_dist_src_dst_pairs += $$(src):$$(dst)) \ $(foreach goal,$(1), \ $(eval _all_dist_goal_output_pairs += $$(goal):$$(dst)))) endef define add_file_name_tag_suffix $(basename $(notdir $1))-FILE_NAME_TAG_PLACEHOLDER$(suffix $1) endef # This function appends suffix FILE_NAME_TAG_PLACEHOLDER from the input file # $(1): a list of goals (e.g. droid, sdk, ndk). These must be PHONY # $(2): the dist files to add to those goals. define dist-for-goals-with-filenametag $(if $(strip $(2)), \ $(foreach file,$(2), \ $(call dist-for-goals,$(1),$(file):$(call add_file_name_tag_suffix,$(file))))) endef .PHONY: shareprojects define __share-projects-rule $(1) : PRIVATE_TARGETS := $(2) $(1): $(2) $(COMPLIANCE_LISTSHARE) $(hide) rm -f $$@ mkdir -p $$(dir $$@) $$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(COMPLIANCE_LISTSHARE) -o $$@ $$(PRIVATE_TARGETS),touch $$@) endef # build list of projects to share in $(1) for meta_lic in $(2) # # $(1): the intermediate project sharing file # $(2): the license metadata to base the sharing on define _share-projects-rule $(eval $(call __share-projects-rule,$(1),$(2))) endef .PHONY: alllicensetexts define __license-texts-rule $(2) : PRIVATE_GOAL := $(1) $(2) : PRIVATE_TARGETS := $(3) $(2) : PRIVATE_ROOTS := $(4) $(2) : PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,licensetexts)/$(2)/arguments $(2): $(3) $(TEXTNOTICE) $(hide) rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(if $$(strip $$(PRIVATE_TARGETS)),$$(call dump-words-to-file,\ -product="$$(PRIVATE_GOAL)" -title="$$(PRIVATE_GOAL)" \ $$(addprefix -strip_prefix ,$$(PRIVATE_ROOTS)) \ -strip_prefix=$(PRODUCT_OUT)/ -strip_prefix=$(HOST_OUT)/\ $$(PRIVATE_TARGETS),\ $$(PRIVATE_ARGUMENT_FILE))) $$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ @$$(PRIVATE_ARGUMENT_FILE),touch $$@) endef # build list of projects to share in $(2) for meta_lic in $(3) for dist goals $(1) # Strip `out/dist/` used as proxy for 'DIST_DIR' # # $(1): the name of the dist goals # $(2): the intermediate project sharing file # $(3): the license metadata to base the sharing on define _license-texts-rule $(eval $(call __license-texts-rule,$(1),$(2),$(3),out/dist/)) endef ########################################################### ## License metadata build rule for dist target $(1) with meta_lic $(2) copied from $(3) ########################################################### define _dist-target-license-metadata-rule $(strip $(eval _meta :=$(2))) $(strip $(eval _dep:=)) # 0p is the indicator for a non-copyrightable file where no party owns the copyright. # i.e. pure data with no copyrightable expression. # If all of the sources are 0p and only 0p, treat the copied file as 0p. Otherwise, all # of the sources must either be 0p or originate from a single metadata file to copy. $(strip $(foreach s,$(strip $(3)),\ $(eval _dmeta:=$(ALL_TARGETS.$(s).META_LIC))\ $(if $(strip $(_dmeta)),\ $(if $(filter-out 0p,$(_dep)),\ $(if $(filter-out $(_dep) 0p,$(_dmeta)),\ $(error cannot copy target from multiple modules: $(1) from $(_dep) and $(_dmeta)),\ $(if $(filter 0p,$(_dep)),$(eval _dep:=$(_dmeta)))),\ $(eval _dep:=$(_dmeta))\ ),\ $(eval TARGETS_MISSING_LICENSE_METADATA += $(s) $(1))))) ifeq (0p,$(strip $(_dep))) # Not copyrightable. No emcumbrances, no license text, no license kind etc. $(_meta): PRIVATE_CONDITIONS := unencumbered $(_meta): PRIVATE_SOURCES := $(3) $(_meta): PRIVATE_INSTALLED := $(1) # use `$(1)` which is the unique and relatively short `out/dist/$(target)` $(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,notice)/$(1)/arguments $(_meta): $(BUILD_LICENSE_METADATA) $(_meta) : rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(call dump-words-to-file,\ $$(addprefix -c ,$$(PRIVATE_CONDITIONS))\ $$(addprefix -s ,$$(PRIVATE_SOURCES))\ $$(addprefix -t ,$$(PRIVATE_TARGETS))\ $$(addprefix -i ,$$(PRIVATE_INSTALLED)),\ $$(PRIVATE_ARGUMENT_FILE)) OUT_DIR=$(OUT_DIR) $(BUILD_LICENSE_METADATA) \ @$$(PRIVATE_ARGUMENT_FILE) \ -o $$@ else ifneq (,$(strip $(_dep))) # Not a missing target, copy metadata and `is_container` etc. from license metadata file `$(_dep)` $(_meta): PRIVATE_DEST_TARGET := $(1) $(_meta): PRIVATE_SOURCE_TARGETS := $(3) $(_meta): PRIVATE_SOURCE_METADATA := $(_dep) # use `$(1)` which is the unique and relatively short `out/dist/$(target)` $(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,copynotice)/$(1)/arguments $(_meta) : $(_dep) $(COPY_LICENSE_METADATA) rm -f $$@ mkdir -p $$(dir $$@) mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE)) $$(call dump-words-to-file,\ $$(addprefix -i ,$$(PRIVATE_DEST_TARGET))\ $$(addprefix -s ,$$(PRIVATE_SOURCE_TARGETS))\ $$(addprefix -d ,$$(PRIVATE_SOURCE_METADATA)),\ $$(PRIVATE_ARGUMENT_FILE)) OUT_DIR=$(OUT_DIR) $(COPY_LICENSE_METADATA) \ @$$(PRIVATE_ARGUMENT_FILE) \ -o $$@ endif endef # use `out/dist/` as a proxy for 'DIST_DIR' define _add_projects_to_share $(strip $(eval _mdir := $(call intermediates-dir-for,METAPACKAGING,meta)/out/dist)) \ $(strip $(eval _idir := $(call intermediates-dir-for,METAPACKAGING,shareprojects))) \ $(strip $(eval _tdir := $(call intermediates-dir-for,METAPACKAGING,licensetexts))) \ $(strip $(eval _allt := $(sort $(foreach goal,$(_all_dist_goal_output_pairs),$(call word-colon,2,$(goal)))))) \ $(foreach target,$(_allt), \ $(eval _goals := $(sort $(foreach dg,$(filter %:$(target),$(_all_dist_goal_output_pairs)),$(call word-colon,1,$(dg))))) \ $(eval _srcs := $(sort $(foreach sdp,$(filter %:$(target),$(_all_dist_src_dst_pairs)),$(call word-colon,1,$(sdp))))) \ $(eval $(call _dist-target-license-metadata-rule,out/dist/$(target),$(_mdir)/out/dist/$(target).meta_lic,$(_srcs))) \ $(eval _f := $(_idir)/$(target).shareprojects) \ $(eval _n := $(_tdir)/$(target).txt) \ $(eval $(call dist-for-goals,$(_goals),$(_f):shareprojects/$(target).shareprojects)) \ $(eval $(call dist-for-goals,$(_goals),$(_n):licensetexts/$(target).txt)) \ $(eval $(call _share-projects-rule,$(_f),$(foreach t, $(filter-out $(TARGETS_MISSING_LICENSE_METADATA),out/dist/$(target)),$(_mdir)/$(t).meta_lic))) \ $(eval $(call _license-texts-rule,$(_goals),$(_n),$(foreach t,$(filter-out $(TARGETS_MISSING_LICENSE_METADATA),out/dist/$(target)),$(_mdir)/$(t).meta_lic))) \ ) endef #------------------------------------------------------------------ # To be used at the end of the build to collect all the uses of # dist-for-goals, and write them into a file for the packaging step to use. # $(1): The file to write define dist-write-file $(strip \ $(call _add_projects_to_share)\ $(if $(strip $(ANDROID_REQUIRE_LICENSE_METADATA)),\ $(foreach target,$(sort $(TARGETS_MISSING_LICENSE_METADATA)),$(warning target $(target) missing license metadata))\ $(if $(strip $(TARGETS_MISSING_LICENSE_METADATA)),\ $(if $(filter true error,$(ANDROID_REQUIRE_LICENSE_METADATA)),\ $(error $(words $(sort $(TARGETS_MISSING_LICENSE_METADATA))) targets need license metadata))))\ $(foreach t,$(sort $(ALL_NON_MODULES)),$(call record-missing-non-module-dependencies,$(t))) \ $(eval $(call report-missing-licenses-rule)) \ $(eval $(call report-all-notice-library-names-rule)) \ $(KATI_obsolete_var dist-for-goals,Cannot be used after dist-write-file) \ $(foreach goal,$(sort $(_all_dist_goals)), \ $(eval $$(goal): _dist_$$(goal))) \ $(shell mkdir -p $(dir $(1))) \ $(file >$(1).tmp, \ DIST_GOAL_OUTPUT_PAIRS := $(sort $(_all_dist_goal_output_pairs)) \ $(newline)DIST_SRC_DST_PAIRS := $(sort $(_all_dist_src_dst_pairs))) \ $(shell if ! cmp -s $(1).tmp $(1); then \ mv $(1).tmp $(1); \ else \ rm $(1).tmp; \ fi)) endef .KATI_READONLY := dist-for-goals dist-write-file dist-for-goals-with-filenametag ================================================ FILE: core/dumpconfig.mk ================================================ # Read and dump the product configuration. # Called from the product-config tool, not from the main build system. # # Ensure we are being called correctly # ifndef KATI $(warning Kati must be used to call dumpconfig.mk, not make.) $(error stopping) endif ifdef DEFAULT_GOAL $(warning Calling dumpconfig.mk from inside the make build system is not) $(warning supported. It is only meant to be called via kati by product-confing.) $(error stopping) endif ifndef TARGET_PRODUCT $(warning dumpconfig.mk requires TARGET_PRODUCT to be set) $(error stopping) endif ifndef TARGET_BUILD_VARIANT $(warning dumpconfig.mk requires TARGET_BUILD_VARIANT to be set) $(error stopping) endif ifneq (build/make/core/config.mk,$(wildcard build/make/core/config.mk)) $(warning dumpconfig must be called from the root of the source tree) $(error stopping) endif ifeq (,$(DUMPCONFIG_FILE)) $(warning dumpconfig requires DUMPCONFIG_FILE to be set) $(error stopping) endif # Skip the second inclusion of all of the product config files, because # we will do these checks in the product_config tool. SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK := true # Before we do anything else output the format version. $(file > $(DUMPCONFIG_FILE),dumpconfig_version,1) $(file >> $(DUMPCONFIG_FILE),dumpconfig_file,$(DUMPCONFIG_FILE)) # Default goal for dumpconfig dumpconfig: $(file >> $(DUMPCONFIG_FILE),***DONE***) @echo ***DONE*** # TODO(Remove): These need to be set externally OUT_DIR := out TMPDIR = /tmp/build-temp BUILD_DATETIME_FILE := $(OUT_DIR)/build_date.txt # Escape quotation marks for CSV, and wraps in quotation marks. define escape-for-csv "$(subst ","",$(subst $(newline), ,$1))" endef # Args: # $(1): include stack define dump-import-start $(eval $(file >> $(DUMPCONFIG_FILE),import,$(strip $(1)))) endef # Args: # $(1): include stack define dump-import-done $(eval $(file >> $(DUMPCONFIG_FILE),imported,$(strip $(1)),$(filter-out $(1),$(MAKEFILE_LIST)))) endef # Args: # $(1): Current file # $(2): Inherited file define dump-inherit $(eval $(file >> $(DUMPCONFIG_FILE),inherit,$(strip $(1)),$(strip $(2)))) endef # Args: # $(1): Config phase (PRODUCT, EXPAND, or DEVICE) # $(2): Root nodes to import # $(3): All variable names # $(4): Single-value variables # $(5): Makefile being processed define dump-phase-start $(eval $(file >> $(DUMPCONFIG_FILE),phase,$(strip $(1)),$(strip $(2)))) \ $(foreach var,$(3), \ $(eval $(file >> $(DUMPCONFIG_FILE),var,$(if $(filter $(4),$(var)),single,list),$(var))) \ ) \ $(call dump-config-vals,$(strip $(5)),initial) endef # Args: # $(1): Makefile being processed define dump-phase-end $(call dump-config-vals,$(strip $(1)),final) endef define dump-debug $(eval $(file >> $(DUMPCONFIG_FILE),debug,$(1))) endef # Skip these when dumping. They're not used and they cause a lot of noise in the dump. DUMPCONFIG_SKIP_VARS := \ .VARIABLES \ .KATI_SYMBOLS \ 1 \ 2 \ 3 \ 4 \ 5 \ 6 \ 7 \ 8 \ 9 \ LOCAL_PATH \ MAKEFILE_LIST \ current_mk \ _eiv_ev \ _eiv_i \ _eiv_sv \ _eiv_tv \ inherit_var \ np \ _node_import_context \ _included \ _include_stack \ _in \ _nic.% # Args: # $(1): Makefile that was included # $(2): block (before,import,after,initial,final) define dump-config-vals $(foreach var,$(filter-out $(DUMPCONFIG_SKIP_VARS),$(.KATI_SYMBOLS)),\ $(eval $(file >> $(DUMPCONFIG_FILE),val,$(call escape-for-csv,$(1)),$(2),$(call escape-for-csv,$(var)),$(call escape-for-csv,$($(var))),$(call escape-for-csv,$(KATI_variable_location $(var))))) \ ) endef include build/make/core/config.mk ================================================ FILE: core/dumpvar.mk ================================================ # --------------------------------------------------------------- # the setpath shell function in envsetup.sh uses this to figure out # what to add to the path given the config we have chosen. ifeq ($(CALLED_FROM_SETUP),true) ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG) ANDROID_GCC_PREBUILTS := prebuilts/gcc/$(HOST_PREBUILT_TAG) ANDROID_CLANG_PREBUILTS := prebuilts/clang/host/$(HOST_PREBUILT_TAG) # Dump mulitple variables to "=" pairs, one per line. # The output may be executed as bash script. # Input variables: # DUMP_MANY_VARS: the list of variable names. # DUMP_VAR_PREFIX: an optional prefix of the variable name added to the output. # The value is printed in parts because large variables like PRODUCT_PACKAGES # can exceed the maximum linux command line size .PHONY: dump-many-vars dump-many-vars : @$(foreach v, $(DUMP_MANY_VARS),\ printf "%s='%s" '$(DUMP_VAR_PREFIX)$(v)' '$(firstword $($(v)))'; \ $(foreach part, $(wordlist 2, $(words $($(v))), $($(v))),\ printf " %s" '$(part)'$(newline))\ printf "'\n";) endif # CALLED_FROM_SETUP ifneq (,$(RBC_DUMP_CONFIG_FILE)) $(call dump-variables-rbc,$(RBC_DUMP_CONFIG_FILE)) endif ================================================ FILE: core/dupcheck.sh ================================================ #!/bin/bash # Find duplicate shared libraries by md5 checksum and possible duplicates by size. # Results will be available in the out directory of the build. # Usage: # ./dupcheck.sh OUT_DIR="$1" IMG="$2" TMP_MD5="${OUT_DIR}/_dup_md5" TMP_SIZE="${OUT_DIR}/_dup_size" TMP_CHECK="${OUT_DIR}/_dup_tmp_check" TMP_SIZE_REAL="${OUT_DIR}/_dup_size_real" TMP_FILE1="${OUT_DIR}/_dup_f1" TMP_FILE2="${OUT_DIR}/_dup_f2" MD5_DUPLICATES="${OUT_DIR}/duplicate-libs-md5-${IMG}.txt" SIZE_DUPLICATES="${OUT_DIR}/duplicate-libs-size-${IMG}.txt" # Check arguments if [ "$#" -ne 2 ]; then echo "Usage: ./dupcheck.sh " exit 1 fi # Check host and toolchain version CHECK_HOST=$(uname) if [ "${CHECK_HOST}" == "Linux" ]; then ARCH="linux-x86" else ARCH="darwin-x86" fi BINUTILS_PATH="./prebuilts/clang/host/${ARCH}/llvm-binutils-stable" # Remove any old files if they exist. if [ -f "${MD5_DUPLICATES}" ]; then rm "${MD5_DUPLICATES}" fi if [ -f "${SIZE_DUPLICATES}" ]; then rm "${SIZE_DUPLICATES}" fi # Find all .so files and calculate their md5. find ./"${OUT_DIR}"/${IMG}/ -name "lib*.so" -type f -print0 | xargs -0 md5sum | sed -e "s# .*/# #" | sort | uniq -c | sort -g | sed "/^.*1 /d" | sed "s/^. *[0-9] //" > "${TMP_MD5}" 2>&1 if [ -s "${TMP_MD5}" ]; then while read -r list; do checksum=$(echo "${list}" | cut -f1 -d ' ') filename=$(echo "${list}" | cut -f2 -d ' ') # For each md5, list the file paths that match. { echo "MD5: ${checksum}"; \ find ./"${OUT_DIR}"/${IMG}/ -name "${filename}" -type f -print0 | xargs -0 md5sum | grep "${checksum}" | sed 's/^.* //'; \ echo ""; \ } >> "${MD5_DUPLICATES}" done <"${TMP_MD5}" else echo "No duplicate files by md5 found." >> "${MD5_DUPLICATES}" fi # Cleanup rm "${TMP_MD5}" # Find possible duplicate .so files by size. find ./"${OUT_DIR}"/${IMG}/ -name "*.so" -type f -print0 | xargs -0 stat --format="%s %n" 2>/dev/null | sed -e "s# .*/# #" | sort | uniq -c | sort -g | sed "/^.*1 /d" > "${TMP_SIZE}" 2>&1 if [ -s "${TMP_SIZE}" ]; then while read -r list; do size=$(echo "${list}" | cut -f2 -d ' ') filename=$(echo "${list}" | cut -f3 -d ' ') # Check if the files are not in the md5sum list and do nothing if that is the case. find ./"${OUT_DIR}"/${IMG}/ -name "${filename}" -type f -print0 | xargs -0 stat --format="%s %n" 2>/dev/null | grep "${size}" | sed "s/^.* //" | sort > "${TMP_CHECK}" 2>&1 while read -r filepath; do found=$(grep -F "${filepath}" "${MD5_DUPLICATES}") if [ -z "${found}" ]; then echo "${filepath}" >> "${TMP_SIZE_REAL}" fi done<"${TMP_CHECK}" # For every duplication found, diff the .note and .text sections. if [ -s "${TMP_SIZE_REAL}" ]; then { echo "File: ${filename}, Size: ${size}"; \ cat "${TMP_SIZE_REAL}"; \ echo ""; \ } >> "${SIZE_DUPLICATES}" count=$(wc -l "${TMP_SIZE_REAL}" | cut -f1 -d ' ') # Limitation: this only works for file pairs. If more than two possible duplications are found, the user need to check manually # all the possible combinations using the llvm-readelf and llvm-objdump commands below. if [ "${count}" = 2 ]; then file1=$(head -n 1 "${TMP_SIZE_REAL}") file2=$(tail -n 1 "${TMP_SIZE_REAL}") # Check .note section ${BINUTILS_PATH}/llvm-readelf --wide --notes "${file1}" > "${TMP_FILE1}" 2>&1 ${BINUTILS_PATH}/llvm-readelf --wide --notes "${file2}" > "${TMP_FILE2}" 2>&1 { diff -u "${TMP_FILE1}" "${TMP_FILE2}" | sed "1d;2d;3d"; \ echo ""; } >> "${SIZE_DUPLICATES}" # Check .text section ${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text "${file1}" | sed "1d;2d"> "${TMP_FILE1}" 2>&1 ${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text "${file2}" | sed "1d;2d"> "${TMP_FILE2}" 2>&1 { diff -u "${TMP_FILE1}" "${TMP_FILE2}" | sed "1d;2d;3d"; \ echo ""; } >> "${SIZE_DUPLICATES}" # Cleanup rm "${TMP_FILE1}" "${TMP_FILE2}" else echo "*Note: more than one duplicate. Manually verify all possible combinations." >> "${SIZE_DUPLICATES}" fi rm "${TMP_SIZE_REAL}" echo "" >> "${SIZE_DUPLICATES}" fi done <"${TMP_SIZE}" # Cleanup rm "${TMP_SIZE}" "${TMP_CHECK}" else echo "No duplicate files by size found." >> "${SIZE_DUPLICATES}" fi ================================================ FILE: core/dynamic_binary.mk ================================================ ########################################################### ## Standard rules for building any target-side binaries ## with dynamic linkage (dynamic libraries or executables ## that link with dynamic libraries) ## ## Files including this file must define a rule to build ## the target $(linked_module). ########################################################### # This constraint means that we can hard-code any $(TARGET_*) variables. ifdef LOCAL_IS_HOST_MODULE $(error This file should not be used to build host binaries. Included by (or near) $(lastword $(filter-out config/%,$(MAKEFILE_LIST)))) endif # The name of the target file, without any path prepended. # This duplicates logic from base_rules.mk because we need to # know its results before base_rules.mk is included. include $(BUILD_SYSTEM)/configure_module_stem.mk intermediates := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX)) # Define the target that is the unmodified output of the linker. # The basename of this target must be the same as the final output # binary name, because it's used to set the "soname" in the binary. # The includer of this file will define a rule to build this target. linked_module := $(intermediates)/LINKED/$(notdir $(my_installed_module_stem)) # This tells binary.make to explicitly define the PRIVATE_ variables for # linked_module as well as for LOCAL_BUILT_MODULE. LOCAL_INTERMEDIATE_TARGETS := $(linked_module) ################################### include $(BUILD_SYSTEM)/use_lld_setup.mk include $(BUILD_SYSTEM)/binary.mk ################################### ifdef LOCAL_INJECT_BSSL_HASH inject_module := $(intermediates)/INJECT_BSSL_HASH/$(notdir $(my_installed_module_stem)) LOCAL_INTERMEDIATE_TARGETS += $(inject_module) $(inject_module): $(SOONG_HOST_OUT)/bin/bssl_inject_hash $(inject_module): $(linked_module) @echo "target inject BSSL hash: $(PRIVATE_MODULE) ($@)" $(SOONG_HOST_OUT)/bin/bssl_inject_hash -in-object $< -o $@ else inject_module := $(linked_module) endif ########################################################### ## Store a copy with symbols for symbolic debugging ########################################################### ifeq ($(LOCAL_UNSTRIPPED_PATH),) my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) else my_unstripped_path := $(LOCAL_UNSTRIPPED_PATH) endif symbolic_input := $(inject_module) symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem) elf_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto) ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output) ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_mapping_path) $(eval $(call copy-unstripped-elf-file-with-mapping,$(symbolic_input),$(symbolic_output),$(elf_mapping_path))) ########################################################### ## Store breakpad symbols ########################################################### ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) my_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) breakpad_input := $(inject_module) breakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym $(breakpad_output) : $(breakpad_input) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF) @echo "target breakpad: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \ $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \ else \ echo "skipped for non-elf file."; \ touch $@; \ fi $(LOCAL_BUILT_MODULE) : $(breakpad_output) endif ########################################################### ## Strip ########################################################### strip_input := $(inject_module) strip_output := $(LOCAL_BUILT_MODULE) # Use an order-only dependency to ensure the unstripped file in the symbols # directory is copied when the module is built, but does not force the # module to be rebuilt when the symbols directory is cleaned by installclean. $(strip_output): | $(symbolic_output) my_strip_module := $(firstword \ $(LOCAL_STRIP_MODULE_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ $(LOCAL_STRIP_MODULE)) ifeq ($(my_strip_module),) my_strip_module := mini-debug-info endif ifeq ($(my_strip_module),false) my_strip_module := endif my_strip_args := ifeq ($(my_strip_module),mini-debug-info) my_strip_args += --keep-mini-debug-info else ifeq ($(my_strip_module),keep_symbols) my_strip_args += --keep-symbols endif ifeq (,$(filter no_debuglink mini-debug-info,$(my_strip_module))) ifneq ($(TARGET_BUILD_VARIANT),user) my_strip_args += --add-gnu-debuglink endif endif ifeq ($($(my_prefix)OS),darwin) # llvm-strip does not support Darwin Mach-O yet. my_strip_args += --use-gnu-strip endif valid_strip := mini-debug-info keep_symbols true no_debuglink ifneq (,$(filter-out $(valid_strip),$(my_strip_module))) $(call pretty-error,Invalid strip value $(my_strip_module), only one of $(valid_strip) allowed) endif ifneq (,$(my_strip_module)) $(strip_output): PRIVATE_STRIP_ARGS := $(my_strip_args) $(strip_output): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX) $(strip_output): $(strip_input) $(SOONG_STRIP_PATH) $(XZ) @echo "$($(PRIVATE_PREFIX)DISPLAY) Strip: $(PRIVATE_MODULE) ($@)" CLANG_BIN=$(LLVM_PREBUILTS_PATH) \ CROSS_COMPILE=$(PRIVATE_TOOLS_PREFIX) \ XZ=$(XZ) \ CREATE_MINIDEBUGINFO=${CREATE_MINIDEBUGINFO} \ $(SOONG_STRIP_PATH) -i $< -o $@ -d $@.strip.d $(PRIVATE_STRIP_ARGS) ifneq ($(HOST_OS),darwin) $(strip_output): $(CREATE_MINIDEBUGINFO) endif $(call include-depfile,$(strip_output).strip.d,$(strip_output)) else # Don't strip the binary, just copy it. We can't skip this step # because a copy of the binary must appear at LOCAL_BUILT_MODULE. $(strip_output): $(strip_input) @echo "target Unstripped: $(PRIVATE_MODULE) ($@)" $(copy-file-to-target) endif # my_strip_module $(cleantarget): PRIVATE_CLEAN_FILES += \ $(linked_module) \ $(inject_module) \ $(breakpad_output) \ $(symbolic_output) \ $(strip_output) ================================================ FILE: core/empty_test_config.xml ================================================ ================================================ FILE: core/envsetup.mk ================================================ # Variables we check: # HOST_BUILD_TYPE = { release debug } # TARGET_BUILD_TYPE = { release debug } # and we output a bunch of variables, see the case statement at # the bottom for the full list # OUT_DIR is also set to "out" if it's not already set. # this allows you to set it to somewhere else if you like # SCAN_EXCLUDE_DIRS is an optional, whitespace separated list of # directories that will also be excluded from full checkout tree # searches for source or make files, in addition to OUT_DIR. # This can be useful if you set OUT_DIR to be a different directory # than other outputs of your build system. # Returns all words in $1 up to and including $2 define find_and_earlier $(strip $(if $(1), $(firstword $(1)) $(if $(filter $(firstword $(1)),$(2)),, $(call find_and_earlier,$(wordlist 2,$(words $(1)),$(1)),$(2))))) endef #$(warning $(call find_and_earlier,A B C,A)) #$(warning $(call find_and_earlier,A B C,B)) #$(warning $(call find_and_earlier,A B C,C)) #$(warning $(call find_and_earlier,A B C,D)) # Runs a starlark file, and sets all the variables in its top-level # variables_to_export_to_make variable as make variables. # # In order to avoid running starlark every time the stamp file is checked, we use # $(KATI_shell_no_rerun). Then, to make sure that we actually do rerun kati when # modifying the starlark files, we add the starlark files to the kati stamp file with # $(KATI_extra_file_deps). # # Arguments: # $(1): A single starlark file to use as the entrypoint # $(2): An optional list of starlark files to NOT include as kati dependencies. # $(3): An optional list of extra flags to pass to rbcrun define run-starlark $(eval _starlark_results := $(OUT_DIR)/starlark_results/$(subst /,_,$(1)).mk) $(KATI_shell_no_rerun mkdir -p $(OUT_DIR)/starlark_results && $(OUT_DIR)/rbcrun --mode=make $(3) $(1) >$(_starlark_results) && touch -t 200001010000 $(_starlark_results)) $(if $(filter-out 0,$(.SHELLSTATUS)),$(error Starlark failed to run)) $(eval include $(_starlark_results)) $(KATI_extra_file_deps $(filter-out $(2),$(LOADED_STARLARK_FILES))) $(eval LOADED_STARLARK_FILES :=) $(eval _starlark_results :=) endef # --------------------------------------------------------------- # Release config include $(BUILD_SYSTEM)/release_config.mk # --------------------------------------------------------------- # Set up version information include $(BUILD_SYSTEM)/version_util.mk # This used to be calculated, but is now fixed and not expected # to change over time anymore. New code attempting to use a # variable like IS_AT_LAST_* should instead use a # build system flag. ENABLED_VERSIONS := "OPR1 OPD1 OPD2 OPM1 OPM2 PPR1 PPD1 PPD2 PPM1 PPM2 QPR1 QP1A QP1B QP2A QP2B QD1A QD1B QD2A QD2B QQ1A QQ1B QQ2A QQ2B QQ3A QQ3B RP1A RP1B RP2A RP2B RD1A RD1B RD2A RD2B RQ1A RQ1B RQ2A RQ2B RQ3A RQ3B SP1A SP1B SP2A SP2B SD1A SD1B SD2A SD2B SQ1A SQ1B SQ2A SQ2B SQ3A SQ3B TP1A TP1B TP2A TP2B TD1A TD1B TD2A TD2B TQ1A TQ1B TQ2A TQ2B TQ3A TQ3B UP1A UP1B UP2A UP2B UD1A UD1B UD2A UD2B UQ1A UQ1B UQ2A UQ2B UQ3A UQ3B" $(foreach v,$(ENABLED_VERSIONS), \ $(eval IS_AT_LEAST_$(v) := true)) # --------------------------------------------------------------- # If you update the build system such that the environment setup # or buildspec.mk need to be updated, increment this number, and # people who haven't re-run those will have to do so before they # can build. Make sure to also update the corresponding value in # buildspec.mk.default and envsetup.sh. CORRECT_BUILD_ENV_SEQUENCE_NUMBER := 13 # --------------------------------------------------------------- # The product defaults to generic on hardware ifeq ($(TARGET_PRODUCT),) TARGET_PRODUCT := aosp_arm64 endif # the variant -- the set of files that are included for a build ifeq ($(strip $(TARGET_BUILD_VARIANT)),) TARGET_BUILD_VARIANT := eng endif TARGET_BUILD_APPS ?= TARGET_BUILD_UNBUNDLED_IMAGE ?= # Set to true for an unbundled build, i.e. a build without # support for platform targets like the system image. This also # disables consistency checks that only apply to full platform # builds. TARGET_BUILD_UNBUNDLED ?= # TARGET_BUILD_APPS implies unbundled build, otherwise we default # to bundled (i.e. platform targets such as the system image are # included). ifneq ($(TARGET_BUILD_APPS),) TARGET_BUILD_UNBUNDLED := true endif # TARGET_BUILD_UNBUNDLED_IMAGE also implies unbundled build. # (i.e. it targets to only unbundled image, such as the vendor image, # ,or the product image). ifneq ($(TARGET_BUILD_UNBUNDLED_IMAGE),) TARGET_BUILD_UNBUNDLED := true endif .KATI_READONLY := \ TARGET_PRODUCT \ TARGET_BUILD_VARIANT \ TARGET_BUILD_APPS \ TARGET_BUILD_UNBUNDLED \ TARGET_BUILD_UNBUNDLED_IMAGE \ # --------------------------------------------------------------- # Set up configuration for host machine. We don't do cross- # compiles except for arm, so the HOST is whatever we are # running on # HOST_OS ifneq (,$(findstring Linux,$(UNAME))) HOST_OS := linux endif ifneq (,$(findstring Darwin,$(UNAME))) HOST_OS := darwin endif ifeq ($(CALLED_FROM_SETUP),true) HOST_OS_EXTRA := $(shell uname -rsm) ifeq ($(HOST_OS),linux) ifneq ($(wildcard /etc/os-release),) HOST_OS_EXTRA += $(shell source /etc/os-release; echo $$PRETTY_NAME) endif else ifeq ($(HOST_OS),darwin) HOST_OS_EXTRA += $(shell sw_vers -productVersion) endif HOST_OS_EXTRA := $(subst $(space),-,$(HOST_OS_EXTRA)) endif # BUILD_OS is the real host doing the build. BUILD_OS := $(HOST_OS) # We can do the cross-build only on Linux ifeq ($(HOST_OS),linux) # Windows has been the default host_cross OS ifeq (,$(filter-out windows,$(HOST_CROSS_OS))) # We can only create static host binaries for Linux, so if static host # binaries are requested, turn off Windows cross-builds. ifeq ($(BUILD_HOST_static),) HOST_CROSS_OS := windows HOST_CROSS_ARCH := x86 HOST_CROSS_2ND_ARCH := x86_64 2ND_HOST_CROSS_IS_64_BIT := true endif else ifeq ($(HOST_CROSS_OS),linux_bionic) ifeq (,$(HOST_CROSS_ARCH)) $(error HOST_CROSS_ARCH missing.) endif else $(error Unsupported HOST_CROSS_OS $(HOST_CROSS_OS)) endif else ifeq ($(HOST_OS),darwin) HOST_CROSS_OS := darwin HOST_CROSS_ARCH := arm64 HOST_CROSS_2ND_ARCH := endif ifeq ($(HOST_OS),) $(error Unable to determine HOST_OS from uname -sm: $(UNAME)!) endif # HOST_ARCH ifneq (,$(findstring x86_64,$(UNAME))) HOST_ARCH := x86_64 HOST_2ND_ARCH := x86 HOST_IS_64_BIT := true else ifneq (,$(findstring i686,$(UNAME))$(findstring x86,$(UNAME))) $(error Building on a 32-bit x86 host is not supported: $(UNAME)!) endif endif ifeq ($(HOST_OS),darwin) # Mac no longer supports 32-bit executables HOST_2ND_ARCH := endif HOST_2ND_ARCH_VAR_PREFIX := 2ND_ HOST_2ND_ARCH_MODULE_SUFFIX := _32 HOST_CROSS_2ND_ARCH_VAR_PREFIX := 2ND_ HOST_CROSS_2ND_ARCH_MODULE_SUFFIX := _64 TARGET_2ND_ARCH_VAR_PREFIX := 2ND_ .KATI_READONLY := \ HOST_ARCH \ HOST_2ND_ARCH \ HOST_IS_64_BIT \ HOST_2ND_ARCH_VAR_PREFIX \ HOST_2ND_ARCH_MODULE_SUFFIX \ HOST_CROSS_2ND_ARCH_VAR_PREFIX \ HOST_CROSS_2ND_ARCH_MODULE_SUFFIX \ TARGET_2ND_ARCH_VAR_PREFIX \ combo_target := HOST_ combo_2nd_arch_prefix := include $(BUILD_COMBOS)/select.mk ifdef HOST_2ND_ARCH combo_2nd_arch_prefix := $(HOST_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/combo/select.mk endif # Load the windows cross compiler under Linux ifdef HOST_CROSS_OS combo_target := HOST_CROSS_ combo_2nd_arch_prefix := include $(BUILD_SYSTEM)/combo/select.mk ifdef HOST_CROSS_2ND_ARCH combo_2nd_arch_prefix := $(HOST_CROSS_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/combo/select.mk endif endif # on windows, the tools have .exe at the end, and we depend on the # host config stuff being done first BUILD_ARCH := $(HOST_ARCH) BUILD_2ND_ARCH := $(HOST_2ND_ARCH) ifeq ($(HOST_ARCH),) $(error Unable to determine HOST_ARCH from uname -sm: $(UNAME)!) endif # the host build defaults to release, and it must be release or debug ifeq ($(HOST_BUILD_TYPE),) HOST_BUILD_TYPE := release endif ifneq ($(HOST_BUILD_TYPE),release) ifneq ($(HOST_BUILD_TYPE),debug) $(error HOST_BUILD_TYPE must be either release or debug, not '$(HOST_BUILD_TYPE)') endif endif # We don't want to move all the prebuilt host tools to a $(HOST_OS)-x86_64 dir. HOST_PREBUILT_ARCH := x86 # This is the standard way to name a directory containing prebuilt host # objects. E.g., prebuilt/$(HOST_PREBUILT_TAG)/cc # This must match the logic in get_host_prebuilt_prefix in envsetup.sh HOST_PREBUILT_TAG := $(BUILD_OS)-$(HOST_PREBUILT_ARCH) # TARGET_COPY_OUT_* are all relative to the staging directory, ie PRODUCT_OUT. # Define them here so they can be used in product config files. TARGET_COPY_OUT_SYSTEM := system TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm TARGET_COPY_OUT_SYSTEM_OTHER := system_other TARGET_COPY_OUT_DATA := data TARGET_COPY_OUT_ASAN := $(TARGET_COPY_OUT_DATA)/asan TARGET_COPY_OUT_OEM := oem TARGET_COPY_OUT_RAMDISK := ramdisk TARGET_COPY_OUT_DEBUG_RAMDISK := debug_ramdisk TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK := vendor_debug_ramdisk TARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk TARGET_COPY_OUT_ROOT := root TARGET_COPY_OUT_RECOVERY := recovery # The directory used for optional partitions depend on the BoardConfig, so # they're defined to placeholder values here and swapped after reading the # BoardConfig, to be either the partition dir, or a subdir within 'system'. _vendor_path_placeholder := ||VENDOR-PATH-PH|| _product_path_placeholder := ||PRODUCT-PATH-PH|| _system_ext_path_placeholder := ||SYSTEM_EXT-PATH-PH|| _odm_path_placeholder := ||ODM-PATH-PH|| _vendor_dlkm_path_placeholder := ||VENDOR_DLKM-PATH-PH|| _odm_dlkm_path_placeholder := ||ODM_DLKM-PATH-PH|| _system_dlkm_path_placeholder := ||SYSTEM_DLKM-PATH-PH|| TARGET_COPY_OUT_VENDOR := $(_vendor_path_placeholder) TARGET_COPY_OUT_VENDOR_RAMDISK := vendor_ramdisk TARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK := vendor_kernel_ramdisk TARGET_COPY_OUT_PRODUCT := $(_product_path_placeholder) # TODO(b/135957588) TARGET_COPY_OUT_PRODUCT_SERVICES will copy the target to # product TARGET_COPY_OUT_PRODUCT_SERVICES := $(_product_path_placeholder) TARGET_COPY_OUT_SYSTEM_EXT := $(_system_ext_path_placeholder) TARGET_COPY_OUT_ODM := $(_odm_path_placeholder) TARGET_COPY_OUT_VENDOR_DLKM := $(_vendor_dlkm_path_placeholder) TARGET_COPY_OUT_ODM_DLKM := $(_odm_dlkm_path_placeholder) TARGET_COPY_OUT_SYSTEM_DLKM := $(_system_dlkm_path_placeholder) # Returns the non-sanitized version of the path provided in $1. define get_non_asan_path $(patsubst $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/%,$(PRODUCT_OUT)/%,$1) endef ################################################################# # Set up minimal BOOTCLASSPATH list of jars to build/execute # java code with dalvikvm/art. # Jars present in the ART apex. These should match exactly the list of Java # libraries in art-bootclasspath-fragment. The APEX variant name # (com.android.art) is the same regardless which Soong module provides the ART # APEX. See the long comment in build/soong/java/dexprepopt_bootjars.go for # details. ART_APEX_JARS := \ com.android.art:core-oj \ com.android.art:core-libart \ com.android.art:okhttp \ com.android.art:bouncycastle \ com.android.art:apache-xml # With EMMA_INSTRUMENT_FRAMEWORK=true the Core libraries depend on jacoco. ifeq (true,$(EMMA_INSTRUMENT_FRAMEWORK)) ART_APEX_JARS += com.android.art:jacocoagent endif ################################################################# # Dumps all variables that match [A-Z][A-Z0-9_]* (with a few exceptions) # to the file at $(1). It is used to print only the variables that are # likely to be relevant to the product or board configuration. # Soong config variables are dumped as $(call soong_config_set) calls # instead of the raw variable values, because mk2rbc can't read the # raw ones. There is a final sed command on the output file to # remove leading spaces because I couldn't figure out how to remove # them in pure make code. define dump-variables-rbc $(eval _dump_variables_rbc_excluded := \ BUILD_NUMBER \ DATE \ LOCAL_PATH \ MAKEFILE_LIST \ PRODUCTS \ PRODUCT_COPY_OUT_% \ RBC_PRODUCT_CONFIG \ RBC_BOARD_CONFIG \ SOONG_% \ TARGET_RELEASE \ TOPDIR \ TRACE_BEGIN_SOONG \ USER) $(file >$(OUT_DIR)/dump-variables-rbc-temp.txt,$(subst $(space),$(newline),$(sort $(filter-out $(_dump_variables_rbc_excluded),$(.VARIABLES))))) $(file >$(1),\ $(foreach v, $(shell grep -he "^[A-Z][A-Z0-9_]*$$" $(OUT_DIR)/dump-variables-rbc-temp.txt),\ $(v) := $(strip $($(v)))$(newline))\ $(foreach ns,$(sort $(SOONG_CONFIG_NAMESPACES)),\ $(foreach v,$(sort $(SOONG_CONFIG_$(ns))),\ $$(call soong_config_set,$(ns),$(v),$(SOONG_CONFIG_$(ns)_$(v)))$(newline)))) $(shell sed -i "s/^ *//g" $(1)) endef # Read the product specs so we can get TARGET_DEVICE and other # variables that we need in order to locate the output files. include $(BUILD_SYSTEM)/product_config.mk build_variant := $(filter-out eng user userdebug,$(TARGET_BUILD_VARIANT)) ifneq ($(build_variant)-$(words $(TARGET_BUILD_VARIANT)),-1) $(warning bad TARGET_BUILD_VARIANT: $(TARGET_BUILD_VARIANT)) $(error must be empty or one of: eng user userdebug) endif SDK_HOST_ARCH := x86 TARGET_OS := linux # Some board configuration files use $(PRODUCT_OUT) TARGET_OUT_ROOT := $(OUT_DIR)/target TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE) .KATI_READONLY := TARGET_OUT_ROOT TARGET_PRODUCT_OUT_ROOT PRODUCT_OUT include $(BUILD_SYSTEM)/board_config.mk # the target build type defaults to release ifneq ($(TARGET_BUILD_TYPE),debug) TARGET_BUILD_TYPE := release endif include $(BUILD_SYSTEM)/product_validation_checks.mk # --------------------------------------------------------------- # figure out the output directories SOONG_OUT_DIR := $(OUT_DIR)/soong HOST_OUT_ROOT := $(OUT_DIR)/host .KATI_READONLY := SOONG_OUT_DIR HOST_OUT_ROOT # We want to avoid two host bin directories in multilib build. HOST_OUT := $(HOST_OUT_ROOT)/$(HOST_OS)-$(HOST_PREBUILT_ARCH) # Soong now installs to the same directory as Make. SOONG_HOST_OUT := $(HOST_OUT) HOST_CROSS_OUT := $(HOST_OUT_ROOT)/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH) .KATI_READONLY := HOST_OUT SOONG_HOST_OUT HOST_CROSS_OUT TARGET_COMMON_OUT_ROOT := $(TARGET_OUT_ROOT)/common HOST_COMMON_OUT_ROOT := $(HOST_OUT_ROOT)/common .KATI_READONLY := TARGET_COMMON_OUT_ROOT HOST_COMMON_OUT_ROOT OUT_DOCS := $(TARGET_COMMON_OUT_ROOT)/docs OUT_NDK_DOCS := $(TARGET_COMMON_OUT_ROOT)/ndk-docs .KATI_READONLY := OUT_DOCS OUT_NDK_DOCS $(call KATI_obsolete,BUILD_OUT,Use HOST_OUT instead) BUILD_OUT_EXECUTABLES := $(HOST_OUT)/bin SOONG_HOST_OUT_EXECUTABLES := $(SOONG_HOST_OUT)/bin .KATI_READONLY := BUILD_OUT_EXECUTABLES SOONG_HOST_OUT_EXECUTABLES HOST_OUT_EXECUTABLES := $(HOST_OUT)/bin HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib64 HOST_OUT_DYLIB_LIBRARIES := $(HOST_OUT)/lib64 HOST_OUT_RENDERSCRIPT_BITCODE := $(HOST_OUT_SHARED_LIBRARIES) HOST_OUT_JAVA_LIBRARIES := $(HOST_OUT)/framework HOST_OUT_SDK_ADDON := $(HOST_OUT)/sdk_addon HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest64 HOST_OUT_COVERAGE := $(HOST_OUT)/coverage HOST_OUT_TESTCASES := $(HOST_OUT)/testcases HOST_OUT_ETC := $(HOST_OUT)/etc .KATI_READONLY := \ HOST_OUT_EXECUTABLES \ HOST_OUT_SHARED_LIBRARIES \ HOST_OUT_RENDERSCRIPT_BITCODE \ HOST_OUT_JAVA_LIBRARIES \ HOST_OUT_SDK_ADDON \ HOST_OUT_NATIVE_TESTS \ HOST_OUT_COVERAGE \ HOST_OUT_TESTCASES \ HOST_OUT_ETC HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT)/bin HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib HOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest HOST_CROSS_OUT_COVERAGE := $(HOST_CROSS_OUT)/coverage HOST_CROSS_OUT_TESTCASES := $(HOST_CROSS_OUT)/testcases .KATI_READONLY := \ HOST_CROSS_OUT_EXECUTABLES \ HOST_CROSS_OUT_SHARED_LIBRARIES \ HOST_CROSS_OUT_NATIVE_TESTS \ HOST_CROSS_OUT_COVERAGE \ HOST_CROSS_OUT_TESTCASES HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj HOST_OUT_NOTICE_FILES := $(HOST_OUT_INTERMEDIATES)/NOTICE_FILES HOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj HOST_OUT_FAKE := $(HOST_OUT)/fake_packages .KATI_READONLY := \ HOST_OUT_INTERMEDIATES \ HOST_OUT_NOTICE_FILES \ HOST_OUT_COMMON_INTERMEDIATES \ HOST_OUT_FAKE HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj HOST_CROSS_OUT_NOTICE_FILES := $(HOST_CROSS_OUT_INTERMEDIATES)/NOTICE_FILES .KATI_READONLY := \ HOST_CROSS_OUT_INTERMEDIATES \ HOST_CROSS_OUT_NOTICE_FILES HOST_OUT_GEN := $(HOST_OUT)/gen HOST_OUT_COMMON_GEN := $(HOST_COMMON_OUT_ROOT)/gen .KATI_READONLY := \ HOST_OUT_GEN \ HOST_OUT_COMMON_GEN HOST_CROSS_OUT_GEN := $(HOST_CROSS_OUT)/gen .KATI_READONLY := HOST_CROSS_OUT_GEN # Out for HOST_2ND_ARCH $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj32 $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT)/lib $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES := $(HOST_OUT_EXECUTABLES) $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES := $(HOST_OUT_JAVA_LIBRARIES) $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS := $(HOST_OUT)/nativetest $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES := $(HOST_OUT_TESTCASES) .KATI_READONLY := \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATES \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_SHARED_LIBRARIES \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_EXECUTABLES \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_JAVA_LIBRARIES \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_NATIVE_TESTS \ $(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_TESTCASES # The default host library path. # It always points to the path where we build libraries in the default bitness. HOST_LIBRARY_PATH := $(HOST_OUT_SHARED_LIBRARIES) .KATI_READONLY := HOST_LIBRARY_PATH # Out for HOST_CROSS_2ND_ARCH $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj64 $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES := $(HOST_CROSS_OUT)/lib64 $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES := $(HOST_CROSS_OUT_EXECUTABLES) $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS := $(HOST_CROSS_OUT)/nativetest64 .KATI_READONLY := \ $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATES \ $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_SHARED_LIBRARIES \ $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_EXECUTABLES \ $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_NATIVE_TESTS ifneq ($(filter address,$(SANITIZE_TARGET)),) TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_asan else TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj endif TARGET_OUT_HEADERS := $(TARGET_OUT_INTERMEDIATES)/include .KATI_READONLY := TARGET_OUT_INTERMEDIATES TARGET_OUT_HEADERS ifneq ($(filter address,$(SANITIZE_TARGET)),) TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj_asan else TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj endif .KATI_READONLY := TARGET_OUT_COMMON_INTERMEDIATES TARGET_OUT_GEN := $(PRODUCT_OUT)/gen TARGET_OUT_COMMON_GEN := $(TARGET_COMMON_OUT_ROOT)/gen .KATI_READONLY := TARGET_OUT_GEN TARGET_OUT_COMMON_GEN TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) .KATI_READONLY := TARGET_OUT ifneq ($(filter address,$(SANITIZE_TARGET)),) target_out_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. target_out_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/system else target_out_app_base := $(TARGET_OUT) endif else target_out_shared_libraries_base := $(TARGET_OUT) target_out_app_base := $(TARGET_OUT) endif TARGET_OUT_EXECUTABLES := $(TARGET_OUT)/bin TARGET_OUT_OPTIONAL_EXECUTABLES := $(TARGET_OUT)/xbin ifeq ($(TARGET_IS_64_BIT),true) # /system/lib always contains 32-bit libraries, # and /system/lib64 (if present) always contains 64-bit libraries. TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib64 else TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib endif TARGET_OUT_RENDERSCRIPT_BITCODE := $(TARGET_OUT_SHARED_LIBRARIES) TARGET_OUT_JAVA_LIBRARIES := $(TARGET_OUT)/framework TARGET_OUT_APPS := $(target_out_app_base)/app TARGET_OUT_APPS_PRIVILEGED := $(target_out_app_base)/priv-app TARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout TARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars TARGET_OUT_ETC := $(TARGET_OUT)/etc TARGET_OUT_NOTICE_FILES := $(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES TARGET_OUT_FAKE := $(PRODUCT_OUT)/fake_packages TARGET_OUT_TESTCASES := $(PRODUCT_OUT)/testcases TARGET_OUT_FLAGS := $(TARGET_OUT_INTERMEDIATES)/FLAGS .KATI_READONLY := \ TARGET_OUT_EXECUTABLES \ TARGET_OUT_OPTIONAL_EXECUTABLES \ TARGET_OUT_SHARED_LIBRARIES \ TARGET_OUT_RENDERSCRIPT_BITCODE \ TARGET_OUT_JAVA_LIBRARIES \ TARGET_OUT_APPS \ TARGET_OUT_APPS_PRIVILEGED \ TARGET_OUT_KEYLAYOUT \ TARGET_OUT_KEYCHARS \ TARGET_OUT_ETC \ TARGET_OUT_NOTICE_FILES \ TARGET_OUT_FAKE \ TARGET_OUT_TESTCASES \ TARGET_OUT_FLAGS ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. TARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_OTHER) else TARGET_OUT_SYSTEM_OTHER := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_OTHER) endif .KATI_READONLY := TARGET_OUT_SYSTEM_OTHER # Out for TARGET_2ND_ARCH TARGET_2ND_ARCH_MODULE_SUFFIX := $(HOST_2ND_ARCH_MODULE_SUFFIX) .KATI_READONLY := TARGET_2ND_ARCH_MODULE_SUFFIX ifneq ($(filter address,$(SANITIZE_TARGET)),) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH)_asan else $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj_$(TARGET_2ND_ARCH) endif $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES := $(target_out_shared_libraries_base)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES := $(TARGET_OUT_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS := $(TARGET_OUT_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED := $(TARGET_OUT_APPS_PRIVILEGED) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES := $(TARGET_OUT_TESTCASES) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_APPS_PRIVILEGED \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_TESTCASES MODULE_CLASS_APPS := app MODULE_CLASS_EXECUTABLES := bin MODULE_CLASS_JAVA_LIBRARIES := framework MODULE_CLASS_NATIVE_TESTS := nativetest MODULE_CLASS_METRIC_TESTS := benchmarktest TARGET_OUT_DATA := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA) TARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_EXECUTABLES) TARGET_OUT_DATA_SHARED_LIBRARIES := $(TARGET_OUT_SHARED_LIBRARIES) TARGET_OUT_DATA_JAVA_LIBRARIES := $(TARGET_OUT_DATA)/framework TARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA)/app TARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT) TARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS) TARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC) ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64 TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64 TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest64$(TARGET_VENDOR_TEST_SUFFIX) TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest64$(TARGET_VENDOR_TEST_SUFFIX) else TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX) TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX) endif MODULE_CLASS_FAKE := fake_packages TARGET_OUT_DATA_FAKE := $(TARGET_OUT_DATA)/fake_packages .KATI_READONLY := \ TARGET_OUT_DATA \ TARGET_OUT_DATA_EXECUTABLES \ TARGET_OUT_DATA_SHARED_LIBRARIES \ TARGET_OUT_DATA_JAVA_LIBRARIES \ TARGET_OUT_DATA_APPS \ TARGET_OUT_DATA_KEYLAYOUT \ TARGET_OUT_DATA_KEYCHARS \ TARGET_OUT_DATA_ETC \ TARGET_OUT_DATA_NATIVE_TESTS \ TARGET_OUT_DATA_METRIC_TESTS \ TARGET_OUT_VENDOR_NATIVE_TESTS \ TARGET_OUT_VENDOR_METRIC_TESTS \ TARGET_OUT_DATA_FAKE \ MODULE_CLASS_APPS \ MODULE_CLASS_EXECUTABLES \ MODULE_CLASS_JAVA_LIBRARIES \ MODULE_CLASS_NATIVE_TESTS \ MODULE_CLASS_METRIC_TESTS \ MODULE_CLASS_FAKE $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES := $(TARGET_OUT_DATA_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SHARED_LIBRARIES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS := $(TARGET_OUT_DATA_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest$(TARGET_VENDOR_TEST_SUFFIX) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS := $(TARGET_OUT_DATA)/benchmarktest$(TARGET_VENDOR_TEST_SUFFIX) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_METRIC_TESTS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_NATIVE_TESTS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_METRIC_TESTS \ TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache .KATI_READONLY := TARGET_OUT_CACHE TARGET_OUT_VENDOR := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) .KATI_READONLY := TARGET_OUT_VENDOR ifneq ($(filter address,$(SANITIZE_TARGET)),) target_out_vendor_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR) ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. target_out_vendor_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_VENDOR) else target_out_vendor_app_base := $(TARGET_OUT_VENDOR) endif else target_out_vendor_shared_libraries_base := $(TARGET_OUT_VENDOR) target_out_vendor_app_base := $(TARGET_OUT_VENDOR) endif TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR)/bin TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES := $(TARGET_OUT_VENDOR)/xbin ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib64 else TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib endif TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) TARGET_OUT_VENDOR_JAVA_LIBRARIES := $(TARGET_OUT_VENDOR)/framework TARGET_OUT_VENDOR_APPS := $(target_out_vendor_app_base)/app TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(target_out_vendor_app_base)/priv-app TARGET_OUT_VENDOR_ETC := $(TARGET_OUT_VENDOR)/etc TARGET_OUT_VENDOR_FAKE := $(PRODUCT_OUT)/vendor_fake_packages .KATI_READONLY := \ TARGET_OUT_VENDOR_EXECUTABLES \ TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES \ TARGET_OUT_VENDOR_SHARED_LIBRARIES \ TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \ TARGET_OUT_VENDOR_JAVA_LIBRARIES \ TARGET_OUT_VENDOR_APPS \ TARGET_OUT_VENDOR_APPS_PRIVILEGED \ TARGET_OUT_VENDOR_ETC \ TARGET_OUT_VENDOR_FAKE $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS := $(TARGET_OUT_VENDOR_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(TARGET_OUT_VENDOR_APPS_PRIVILEGED) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS_PRIVILEGED TARGET_OUT_OEM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_OEM) TARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM)/bin ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib64 else TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib endif # We don't expect Java libraries in the oem.img. # TARGET_OUT_OEM_JAVA_LIBRARIES:= $(TARGET_OUT_OEM)/framework TARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM)/app TARGET_OUT_OEM_ETC := $(TARGET_OUT_OEM)/etc .KATI_READONLY := \ TARGET_OUT_OEM \ TARGET_OUT_OEM_EXECUTABLES \ TARGET_OUT_OEM_SHARED_LIBRARIES \ TARGET_OUT_OEM_APPS \ TARGET_OUT_OEM_ETC $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES := $(TARGET_OUT_OEM_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES := $(TARGET_OUT_OEM)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS := $(TARGET_OUT_OEM_APPS) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_OEM_APPS \ TARGET_OUT_ODM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM) ifneq ($(filter address,$(SANITIZE_TARGET)),) target_out_odm_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM) ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. target_out_odm_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_OEM) else target_out_odm_app_base := $(TARGET_OUT_ODM) endif else target_out_odm_shared_libraries_base := $(TARGET_OUT_ODM) target_out_odm_app_base := $(TARGET_OUT_ODM) endif TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM)/bin TARGET_OUT_ODM_OPTIONAL_EXECUTABLES := $(TARGET_OUT_ODM)/xbin ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib64 else TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib endif TARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $(TARGET_OUT_ODM_SHARED_LIBRARIES) TARGET_OUT_ODM_JAVA_LIBRARIES := $(TARGET_OUT_ODM)/framework TARGET_OUT_ODM_APPS := $(target_out_odm_app_base)/app TARGET_OUT_ODM_APPS_PRIVILEGED := $(target_out_odm_app_base)/priv-app TARGET_OUT_ODM_ETC := $(TARGET_OUT_ODM)/etc TARGET_OUT_ODM_FAKE := $(PRODUCT_OUT)/odm_fake_packages .KATI_READONLY := \ TARGET_OUT_ODM \ TARGET_OUT_ODM_EXECUTABLES \ TARGET_OUT_ODM_OPTIONAL_EXECUTABLES \ TARGET_OUT_ODM_SHARED_LIBRARIES \ TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \ TARGET_OUT_ODM_JAVA_LIBRARIES \ TARGET_OUT_ODM_APPS \ TARGET_OUT_ODM_APPS_PRIVILEGED \ TARGET_OUT_ODM_ETC \ TARGET_OUT_ODM_FAKE $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS := $(TARGET_OUT_ODM_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED := $(TARGET_OUT_ODM_APPS_PRIVILEGED) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_APPS_PRIVILEGED TARGET_OUT_VENDOR_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DLKM) TARGET_OUT_VENDOR_DLKM_ETC := $(TARGET_OUT_VENDOR_DLKM)/etc .KATI_READONLY := \ TARGET_OUT_VENDOR_DLKM_ETC # Unlike other partitions, vendor_dlkm should only contain kernel modules. TARGET_OUT_VENDOR_DLKM_EXECUTABLES := TARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES := TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES := TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE := TARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES := TARGET_OUT_VENDOR_DLKM_APPS := TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED := $(KATI_obsolete_var \ TARGET_OUT_VENDOR_DLKM_EXECUTABLES \ TARGET_OUT_VENDOR_DLKM_OPTIONAL_EXECUTABLES \ TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \ TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \ TARGET_OUT_VENDOR_DLKM_JAVA_LIBRARIES \ TARGET_OUT_VENDOR_DLKM_APPS \ TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_DLKM_APPS_PRIVILEGED \ , vendor_dlkm should not contain any executables, libraries, or apps) TARGET_OUT_ODM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM_DLKM) TARGET_OUT_ODM_DLKM_ETC := $(TARGET_OUT_ODM_DLKM)/etc .KATI_READONLY := \ TARGET_OUT_ODM_DLKM_ETC # Unlike other partitions, odm_dlkm should only contain kernel modules. TARGET_OUT_ODM_DLKM_EXECUTABLES := TARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES := TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES := TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE := TARGET_OUT_ODM_DLKM_JAVA_LIBRARIES := TARGET_OUT_ODM_DLKM_APPS := TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED := $(KATI_obsolete_var \ TARGET_OUT_ODM_DLKM_EXECUTABLES \ TARGET_OUT_ODM_DLKM_OPTIONAL_EXECUTABLES \ TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \ TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \ TARGET_OUT_ODM_DLKM_JAVA_LIBRARIES \ TARGET_OUT_ODM_DLKM_APPS \ TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_DLKM_APPS_PRIVILEGED \ , odm_dlkm should not contain any executables, libraries, or apps) TARGET_OUT_SYSTEM_DLKM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM) # Unlike other partitions, system_dlkm should only contain kernel modules. TARGET_OUT_SYSTEM_DLKM_EXECUTABLES := TARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES := TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES := TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE := TARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES := TARGET_OUT_SYSTEM_DLKM_APPS := TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS := $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED := $(KATI_obsolete_var \ TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \ TARGET_OUT_SYSTEM_DLKM_OPTIONAL_EXECUTABLES \ TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \ TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \ TARGET_OUT_SYSTEM_DLKM_JAVA_LIBRARIES \ TARGET_OUT_SYSTEM_DLKM_APPS \ TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_RENDERSCRIPT_BITCODE \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_DLKM_APPS_PRIVILEGED \ , system_dlkm should not contain any executables, libraries, or apps) TARGET_OUT_PRODUCT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT) TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT)/bin .KATI_READONLY := TARGET_OUT_PRODUCT ifneq ($(filter address,$(SANITIZE_TARGET)),) target_out_product_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT) ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. target_out_product_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_PRODUCT) else target_out_product_app_base := $(TARGET_OUT_PRODUCT) endif else target_out_product_shared_libraries_base := $(TARGET_OUT_PRODUCT) target_out_product_app_base := $(TARGET_OUT_PRODUCT) endif ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib64 else TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib endif TARGET_OUT_PRODUCT_JAVA_LIBRARIES := $(TARGET_OUT_PRODUCT)/framework TARGET_OUT_PRODUCT_APPS := $(target_out_product_app_base)/app TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(target_out_product_app_base)/priv-app TARGET_OUT_PRODUCT_ETC := $(TARGET_OUT_PRODUCT)/etc TARGET_OUT_PRODUCT_FAKE := $(TARGET_OUT_PRODUCT)/product_fake_packages .KATI_READONLY := \ TARGET_OUT_PRODUCT_EXECUTABLES \ TARGET_OUT_PRODUCT_SHARED_LIBRARIES \ TARGET_OUT_PRODUCT_JAVA_LIBRARIES \ TARGET_OUT_PRODUCT_APPS \ TARGET_OUT_PRODUCT_APPS_PRIVILEGED \ TARGET_OUT_PRODUCT_ETC \ TARGET_OUT_PRODUCT_FAKE $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS := $(TARGET_OUT_PRODUCT_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(TARGET_OUT_PRODUCT_APPS_PRIVILEGED) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_APPS_PRIVILEGED TARGET_OUT_SYSTEM_EXT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT) ifneq ($(filter address,$(SANITIZE_TARGET)),) target_out_system_ext_shared_libraries_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT) ifeq ($(SANITIZE_LITE),true) # When using SANITIZE_LITE, APKs must not be packaged with sanitized libraries, as they will not # work with unsanitized app_process. For simplicity, generate APKs into /data/asan/. target_out_system_ext_app_base := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ASAN)/$(TARGET_COPY_OUT_SYSTEM_EXT) else target_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT) endif else target_out_system_ext_shared_libraries_base := $(TARGET_OUT_SYSTEM_EXT) target_out_system_ext_app_base := $(TARGET_OUT_SYSTEM_EXT) endif ifeq ($(TARGET_IS_64_BIT),true) TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib64 else TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib endif TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES:= $(TARGET_OUT_SYSTEM_EXT)/framework TARGET_OUT_SYSTEM_EXT_APPS := $(target_out_system_ext_app_base)/app TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(target_out_system_ext_app_base)/priv-app TARGET_OUT_SYSTEM_EXT_ETC := $(TARGET_OUT_SYSTEM_EXT)/etc TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT)/bin TARGET_OUT_SYSTEM_EXT_FAKE := $(PRODUCT_OUT)/system_ext_fake_packages .KATI_READONLY := \ TARGET_OUT_SYSTEM_EXT_EXECUTABLES \ TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \ TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES \ TARGET_OUT_SYSTEM_EXT_APPS \ TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED \ TARGET_OUT_SYSTEM_EXT_ETC \ TARGET_OUT_SYSTEM_EXT_FAKE $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT_EXECUTABLES) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS := $(TARGET_OUT_SYSTEM_EXT_APPS) $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED) .KATI_READONLY := \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS \ $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED TARGET_OUT_BREAKPAD := $(PRODUCT_OUT)/breakpad .KATI_READONLY := TARGET_OUT_BREAKPAD TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib TARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/$(TARGET_COPY_OUT_VENDOR)/lib TARGET_ROOT_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED) TARGET_ROOT_OUT_BIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/bin TARGET_OUT_COVERAGE := $(PRODUCT_OUT)/coverage .KATI_READONLY := \ TARGET_OUT_UNSTRIPPED \ TARGET_OUT_EXECUTABLES_UNSTRIPPED \ TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED \ TARGET_OUT_VENDOR_SHARED_LIBRARIES_UNSTRIPPED \ TARGET_ROOT_OUT_UNSTRIPPED \ TARGET_ROOT_OUT_BIN_UNSTRIPPED \ TARGET_OUT_COVERAGE TARGET_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RAMDISK) TARGET_RAMDISK_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED) TARGET_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DEBUG_RAMDISK) TARGET_VENDOR_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK) TARGET_TEST_HARNESS_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_TEST_HARNESS_RAMDISK) TARGET_SYSTEM_DLKM_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_DLKM) .KATI_READONLY := TARGET_SYSTEM_DLKM_OUT TARGET_VENDOR_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_RAMDISK) TARGET_VENDOR_KERNEL_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_KERNEL_RAMDISK) TARGET_ROOT_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ROOT) TARGET_ROOT_OUT_BIN := $(TARGET_ROOT_OUT)/bin TARGET_ROOT_OUT_ETC := $(TARGET_ROOT_OUT)/etc TARGET_ROOT_OUT_USR := $(TARGET_ROOT_OUT)/usr .KATI_READONLY := \ TARGET_ROOT_OUT \ TARGET_ROOT_OUT_BIN \ TARGET_ROOT_OUT_ETC \ TARGET_ROOT_OUT_USR TARGET_RECOVERY_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RECOVERY) TARGET_RECOVERY_ROOT_OUT := $(TARGET_RECOVERY_OUT)/root .KATI_READONLY := \ TARGET_RECOVERY_OUT \ TARGET_RECOVERY_ROOT_OUT TARGET_SYSLOADER_OUT := $(PRODUCT_OUT)/sysloader TARGET_SYSLOADER_ROOT_OUT := $(TARGET_SYSLOADER_OUT)/root TARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system .KATI_READONLY := \ TARGET_SYSLOADER_OUT \ TARGET_SYSLOADER_ROOT_OUT \ TARGET_SYSLOADER_SYSTEM_OUT TARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer TARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data TARGET_INSTALLER_ROOT_OUT := $(TARGET_INSTALLER_OUT)/root TARGET_INSTALLER_SYSTEM_OUT := $(TARGET_INSTALLER_OUT)/root/system .KATI_READONLY := \ TARGET_INSTALLER_OUT \ TARGET_INSTALLER_DATA_OUT \ TARGET_INSTALLER_ROOT_OUT \ TARGET_INSTALLER_SYSTEM_OUT COMMON_MODULE_CLASSES := TARGET_NOTICE_FILES HOST_NOTICE_FILES HOST_JAVA_LIBRARIES PER_ARCH_MODULE_CLASSES := SHARED_LIBRARIES STATIC_LIBRARIES EXECUTABLES GYP RENDERSCRIPT_BITCODE NATIVE_TESTS HEADER_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES .KATI_READONLY := COMMON_MODULE_CLASSES PER_ARCH_MODULE_CLASSES ifeq ($(CALLED_FROM_SETUP),true) PRINT_BUILD_CONFIG ?= true endif ================================================ FILE: core/executable.mk ================================================ # We don't automatically set up rules to build executables for both # TARGET_ARCH and TARGET_2ND_ARCH. # By default, an executable is built for TARGET_ARCH. # To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32" # To build it for both set LOCAL_MULTILIB := both and specify # LOCAL_MODULE_PATH_32 and LOCAL_MODULE_PATH_64 or LOCAL_MODULE_STEM_32 and # LOCAL_MODULE_STEM_64 ifdef LOCAL_IS_HOST_MODULE $(call pretty-error,BUILD_EXECUTABLE is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_EXECUTABLE instead.) endif my_skip_this_target := ifneq ($(filter address,$(SANITIZE_TARGET)),) ifeq (true,$(LOCAL_FORCE_STATIC_EXECUTABLE)) my_skip_this_target := true else ifeq (false, $(LOCAL_CLANG)) my_skip_this_target := true else ifeq (never, $(LOCAL_SANITIZE)) my_skip_this_target := true endif endif ifneq (true,$(my_skip_this_target)) $(call record-module-type,EXECUTABLE) my_prefix := TARGET_ include $(BUILD_SYSTEM)/multilib.mk ifeq ($(my_module_multilib),both) ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) ifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),) $(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) endif ifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),) $(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) endif endif else #!LOCAL_MULTILIB == both LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true endif ifdef TARGET_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := endif my_skip_non_preferred_arch := # check if preferred arch is supported include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # first arch is supported include $(BUILD_SYSTEM)/executable_internal.mk ifneq ($(my_module_multilib),both) my_skip_non_preferred_arch := true endif endif # check if preferred arch was not supported or asked to build both ifndef my_skip_non_preferred_arch ifdef TARGET_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) # check if non-preferred arch is supported include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # non-preferred arch is supported LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/executable_internal.mk endif endif # TARGET_2ND_ARCH endif # !my_skip_non_preferred_arch || LOCAL_MULTILIB LOCAL_2ND_ARCH_VAR_PREFIX := LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := my_module_arch_supported := endif ================================================ FILE: core/executable_internal.mk ================================================ ########################################################### ## Standard rules for building an executable file. ## ## Additional inputs from base_rules.make: ## None. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := EXECUTABLES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := $(TARGET_EXECUTABLE_SUFFIX) endif ifdef target-executable-hook $(call target-executable-hook) endif skip_build_from_source := ifdef LOCAL_PREBUILT_MODULE_FILE ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) include $(BUILD_SYSTEM)/prebuilt_internal.mk skip_build_from_source := true endif endif ifndef skip_build_from_source include $(BUILD_SYSTEM)/dynamic_binary.mk # Check for statically linked libc ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) ifneq ($(filter $(my_static_libraries),libc),) $(error $(LOCAL_PATH): $(LOCAL_MODULE) is statically linking libc to dynamic executable, please remove libc from static libs or set LOCAL_FORCE_STATIC_EXECUTABLE := true) endif endif # Define PRIVATE_ variables from global vars ifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true) my_target_libcrt_builtins := else my_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) endif ifeq ($(LOCAL_NO_CRT),true) my_target_crtbegin_dynamic_o := my_target_crtbegin_static_o := my_target_crtend_o := else ifeq (true,$(call module-in-vendor-or-product)) my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.vendor) my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.vendor) my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.vendor) else my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic) my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static) my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android) endif ifneq ($(LOCAL_SDK_VERSION),) my_target_crtbegin_dynamic_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_dynamic.sdk.$(my_ndk_crt_version)) my_target_crtbegin_static_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_static.sdk.$(my_ndk_crt_version)) my_target_crtend_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_android.sdk.$(my_ndk_crt_version)) endif $(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins) $(linked_module): PRIVATE_TARGET_CRTBEGIN_DYNAMIC_O := $(my_target_crtbegin_dynamic_o) $(linked_module): PRIVATE_TARGET_CRTBEGIN_STATIC_O := $(my_target_crtbegin_static_o) $(linked_module): PRIVATE_TARGET_CRTEND_O := $(my_target_crtend_o) $(linked_module): PRIVATE_POST_LINK_CMD := $(LOCAL_POST_LINK_CMD) ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true) $(linked_module): $(my_target_crtbegin_static_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX) $(transform-o-to-static-executable) $(PRIVATE_POST_LINK_CMD) else $(linked_module): $(my_target_crtbegin_dynamic_o) $(all_objects) $(all_libraries) $(my_target_crtend_o) $(my_target_libcrt_builtins) $(CLANG_CXX) $(transform-o-to-executable) $(PRIVATE_POST_LINK_CMD) endif ifeq ($(my_native_coverage),true) gcno_suffix := .zip built_whole_gcno_libraries := \ $(foreach lib,$(my_whole_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ $(my_host_cross))/$(lib)$(gcno_suffix)) built_static_gcno_libraries := \ $(foreach lib,$(my_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ $(my_host_cross))/$(lib)$(gcno_suffix)) ifdef LOCAL_IS_HOST_MODULE my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) else my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) endif GCNO_ARCHIVE := $(my_installed_module_stem)$(gcno_suffix) $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries)) $(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries) $(package-coverage-files) $(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE) $(copy-file-to-target) $(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE) endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=EXECUTABLE)) endif # skip_build_from_source ================================================ FILE: core/executable_prefer_symlink.mk ================================================ # include this makefile to create the LOCAL_MODULE symlink to the primary version binary. # but this requires the primary version name specified via LOCAL_MODULE_STEM_32 or LOCAL_MODULE_STEM_64, # and different with the LOCAL_MODULE value # # Note: now only limited to the binaries that will be installed under system/bin directory # Create link to the one used depending on the target # configuration. ifneq ($(LOCAL_IS_HOST_MODULE),true) my_symlink := $(addprefix $(TARGET_OUT)/bin/, $(LOCAL_MODULE)) my_src_binary_name := ifeq ($(TARGET_IS_64_BIT),true) ifeq ($(TARGET_SUPPORTS_64_BIT_APPS)|$(TARGET_SUPPORTS_32_BIT_APPS),true|true) my_src_binary_name := $(LOCAL_MODULE_STEM_64) else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true) # We support only 64 bit apps. my_src_binary_name := $(LOCAL_MODULE_STEM_64) else # We support only 32 bit apps. my_src_binary_name := $(LOCAL_MODULE_STEM_32) endif else my_src_binary_name := $(LOCAL_MODULE_STEM_32) endif else my_symlink := $(addprefix $(HOST_OUT)/bin/, $(LOCAL_MODULE)) my_src_binary_name := $(LOCAL_MODULE_STEM_64) endif $(call symlink-file,$(my_module_path)/$(my_src_binary_name),$(my_src_binary_name),$(my_symlink)) # We need this so that the installed files could be picked up based on the # local module name ALL_MODULES.$(my_register_name).INSTALLED += $(my_symlink) # Create the symlink when you run mm/mmm or "make " $(LOCAL_MODULE) : $(my_symlink) my_symlink := ================================================ FILE: core/filter_symbols.sh ================================================ NM=$1 shift PREFIX=$1 shift SUFFIX=$1 shift while test "$1" != "" do $NM -g -fp $1 | while read -a line do type=${line[1]} # if [[ "$type" != "V" && "$type" != "U" ]]; then #if [[ "$type" != "W" && "$type" != "V" && "$type" != "U" ]]; then echo "$PREFIX${line[0]}$SUFFIX # ${line[1]}" #fi done shift done ================================================ FILE: core/force_aapt2.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Including this makefile will force AAPT2 on, # rewriting some properties to convert standard AAPT usage to AAPT2. ifeq ($(LOCAL_USE_AAPT2),false) $(call pretty-error, LOCAL_USE_AAPT2 := false is no longer supported) endif # Filter out support library resources LOCAL_RESOURCE_DIR := $(filter-out \ prebuilts/sdk/current/% \ frameworks/support/%,\ $(LOCAL_RESOURCE_DIR)) # Filter out unnecessary aapt flags ifneq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS))) LOCAL_AAPT_FLAGS := $(subst --extra-packages=,--extra-packages$(space), \ $(filter-out \ --extra-packages=android.support.% \ --extra-packages=androidx.%, \ $(subst --extra-packages$(space),--extra-packages=,$(LOCAL_AAPT_FLAGS)))) ifeq (,$(filter --extra-packages,$(LOCAL_AAPT_FLAGS))) LOCAL_AAPT_FLAGS := $(filter-out --auto-add-overlay,$(LOCAL_AAPT_FLAGS)) endif endif # AAPT2 is pickier about missing resources. Support library may have references to resources # added in current, so always treat LOCAL_SDK_VERSION := as LOCAL_SDK_RES_VERSION := current. ifneq (,$(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION))) LOCAL_SDK_RES_VERSION := current endif ================================================ FILE: core/fuzz_test.mk ================================================ ########################################### ## A thin wrapper around BUILD_EXECUTABLE ## Common flags for fuzz tests are added. ########################################### $(call record-module-type,FUZZ_TEST) ifdef LOCAL_SDK_VERSION $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.) endif my_fuzzer:=libFuzzer ifdef LOCAL_FUZZ_ENGINE my_fuzzer:=$(LOCAL_FUZZ_ENGINE) else ifdef TARGET_FUZZ_ENGINE my_fuzzer:=$(TARGET_FUZZ_ENGINE) endif LOCAL_SANITIZE += fuzzer ifeq ($(my_fuzzer),libFuzzer) LOCAL_STATIC_LIBRARIES += libFuzzer else $(call pretty-error, Unknown fuzz engine $(my_fuzzer)) endif ifdef LOCAL_MODULE_PATH $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE)) endif ifdef LOCAL_MODULE_PATH_32 $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE)) endif ifdef LOCAL_MODULE_PATH_64 $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE)) endif LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE) LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/fuzzers/$(my_fuzzer)/$(LOCAL_MODULE) ifndef LOCAL_STRIP_MODULE LOCAL_STRIP_MODULE := keep_symbols endif include $(BUILD_EXECUTABLE) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=FUZZ_TEST)) ================================================ FILE: core/generate_enforce_rro.mk ================================================ include $(CLEAR_VARS) enforce_rro_module := $(enforce_rro_source_module)__$(PRODUCT_NAME)__auto_generated_rro_$(enforce_rro_partition) LOCAL_PACKAGE_NAME := $(enforce_rro_module) intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON) rro_android_manifest_file := $(intermediates)/AndroidManifest.xml ifeq (true,$(enforce_rro_source_is_manifest_package_name)) use_package_name_arg := --use-package-name else use_package_name_arg := $(rro_android_manifest_file): $(enforce_rro_source_manifest_package_info) endif $(rro_android_manifest_file): PRIVATE_PACKAGE_INFO := $(enforce_rro_source_manifest_package_info) $(rro_android_manifest_file): PRIVATE_USE_PACKAGE_NAME := $(use_package_name_arg) $(rro_android_manifest_file): PRIVATE_PARTITION := $(enforce_rro_partition) # There should be no duplicate overrides, but just in case, set the priority of # /product overlays to be higher than /vendor, to at least get deterministic results. $(rro_android_manifest_file): PRIVATE_PRIORITY := $(if $(filter product,$(enforce_rro_partition)),1,0) $(rro_android_manifest_file): build/make/tools/generate-enforce-rro-android-manifest.py $(hide) build/make/tools/generate-enforce-rro-android-manifest.py \ --package-info $(PRIVATE_PACKAGE_INFO) \ $(PRIVATE_USE_PACKAGE_NAME) \ --partition $(PRIVATE_PARTITION) \ --priority $(PRIVATE_PRIORITY) \ -o $@ LOCAL_PATH:= $(intermediates) # TODO(b/187404676): remove this condition when the prebuilt for packges exporting resource exists. ifeq (,$(TARGET_BUILD_UNBUNDLED)) ifeq ($(enforce_rro_use_res_lib),true) LOCAL_RES_LIBRARIES := $(enforce_rro_source_module) endif endif LOCAL_FULL_MANIFEST_FILE := $(rro_android_manifest_file) LOCAL_AAPT_FLAGS += --auto-add-overlay --keep-raw-values LOCAL_RESOURCE_DIR := $(enforce_rro_source_overlays) ifeq (product,$(enforce_rro_partition)) LOCAL_PRODUCT_MODULE := true else ifeq (vendor,$(enforce_rro_partition)) LOCAL_VENDOR_MODULE := true else $(error Unsupported partition. Want: [vendor/product] Got: [$(enforce_rro_partition)]) endif ifneq (,$(TARGET_BUILD_UNBUNDLED)) LOCAL_SDK_VERSION := current else ifneq (,$(LOCAL_RES_LIBRARIES)) # Technically we are linking against the app (if only to grab its resources), # and because it's potentially not building against the SDK, we can't either. LOCAL_PRIVATE_PLATFORM_APIS := true else ifeq (framework-res,$(enforce_rro_source_module)) LOCAL_PRIVATE_PLATFORM_APIS := true else LOCAL_SDK_VERSION := current endif include $(BUILD_RRO_PACKAGE) ================================================ FILE: core/header_library.mk ================================================ $(call record-module-type,HEADER_LIBRARY) ifdef LOCAL_IS_HOST_MODULE my_prefix := HOST_ LOCAL_HOST_PREFIX := else my_prefix := TARGET_ endif include $(BUILD_SYSTEM)/multilib.mk ifndef my_module_multilib # libraries default to building for both architecturess my_module_multilib := both endif LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/header_library_internal.mk endif ifdef $(my_prefix)2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for 2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/header_library_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # 2ND_ARCH my_module_arch_supported := ================================================ FILE: core/header_library_internal.mk ================================================ ########################################################### ## Standard rules for building a header library. ## ## Additional inputs from base_rules.make: ## None. ########################################################### LOCAL_MODULE_CLASS := HEADER_LIBRARIES LOCAL_UNINSTALLABLE_MODULE := true ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) $(error $(LOCAL_PATH): Cannot set module stem for a library) endif include $(BUILD_SYSTEM)/binary.mk ifneq ($(strip $(all_objects)),) $(call pretty-error,Header libraries may not have any sources) endif $(LOCAL_BUILT_MODULE): $(hide) touch $@ $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HEADER_LIBRARY)) ================================================ FILE: core/host_executable.mk ================================================ $(call record-module-type,HOST_EXECUTABLE) LOCAL_IS_HOST_MODULE := true my_prefix := HOST_ LOCAL_HOST_PREFIX := include $(BUILD_SYSTEM)/multilib.mk ifndef LOCAL_MODULE_HOST_ARCH ifndef my_module_multilib # By default we only build host module for the first arch. my_module_multilib := first endif endif ifeq ($(my_module_multilib),both) ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) ifeq ($(LOCAL_MODULE_PATH_32)$(LOCAL_MODULE_STEM_32),) $(error $(LOCAL_PATH): LOCAL_MODULE_STEM_32 or LOCAL_MODULE_PATH_32 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) endif ifeq ($(LOCAL_MODULE_PATH_64)$(LOCAL_MODULE_STEM_64),) $(error $(LOCAL_PATH): LOCAL_MODULE_STEM_64 or LOCAL_MODULE_PATH_64 is required for LOCAL_MULTILIB := both for module $(LOCAL_MODULE)) endif endif else #!LOCAL_MULTILIB == both LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true endif LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/host_executable_internal.mk endif ifdef HOST_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for HOST_2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/host_executable_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # HOST_2ND_ARCH LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := my_module_arch_supported := ================================================ FILE: core/host_executable_internal.mk ================================================ ########################################################### ## Standard rules for building an executable file. ## ## Additional inputs from base_rules.make: ## None. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := EXECUTABLES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := $($(my_prefix)EXECUTABLE_SUFFIX) endif ifdef host-executable-hook $(call host-executable-hook) endif skip_build_from_source := ifdef LOCAL_PREBUILT_MODULE_FILE ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) include $(BUILD_SYSTEM)/prebuilt_internal.mk skip_build_from_source := true endif endif ifndef skip_build_from_source include $(BUILD_SYSTEM)/binary.mk my_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) $(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt) my_libdir := $(notdir $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_SHARED_LIBRARIES)) ifeq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) $(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../../$(my_libdir) ../../../$(my_libdir) else $(LOCAL_BUILT_MODULE): PRIVATE_RPATHS := ../$(my_libdir) $(my_libdir) endif my_libdir := my_crtbegin := my_crtend := my_libcrt_builtins := ifdef USE_HOST_MUSL my_crtbegin := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtbegin_dynamic) my_crtend := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtend) my_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) $(LOCAL_BUILT_MODULE): PRIVATE_LDFLAGS += -Wl,--no-dynamic-linker endif $(LOCAL_BUILT_MODULE): PRIVATE_CRTBEGIN := $(my_crtbegin) $(LOCAL_BUILT_MODULE): PRIVATE_CRTEND := $(my_crtend) $(LOCAL_BUILT_MODULE): PRIVATE_LIBCRT_BUILTINS := $(my_libcrt_builtins) $(LOCAL_BUILT_MODULE): $(my_crtbegin) $(my_crtend) $(my_libcrt_builtins) $(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries) $(CLANG_CXX) $(transform-host-o-to-executable) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_EXECUTABLE)) endif # skip_build_from_source ================================================ FILE: core/host_java_library.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call record-module-type,HOST_JAVA_LIBRARY) # # Standard rules for building a host java library. # ####################################### include $(BUILD_SYSTEM)/host_java_library_common.mk ####################################### # Enable emma instrumentation only if the module asks so. ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) ifneq (true,$(EMMA_INSTRUMENT)) LOCAL_EMMA_INSTRUMENT := endif endif full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar full_classes_jar := $(intermediates.COMMON)/classes.jar java_source_list_file := $(intermediates.COMMON)/java-source-list full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar LOCAL_INTERMEDIATE_TARGETS += \ $(full_classes_compiled_jar) \ $(full_classes_jarjar_jar) \ $(java_source_list_file) \ $(full_classes_combined_jar) ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### java_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) \ $(filter %.java,$(LOCAL_GENERATED_SOURCES)) all_java_sources := $(java_sources) ALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources) include $(BUILD_SYSTEM)/java_common.mk # List of dependencies for anything that needs all java sources in place java_sources_deps := \ $(java_sources) \ $(java_resource_sources) \ $(LOCAL_SRCJARS) \ $(LOCAL_ADDITIONAL_DEPENDENCIES) $(java_source_list_file): $(java_sources_deps) $(write-java-source-list) # TODO(b/143658984): goma can't handle the --system argument to javac. #$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL) $(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) $(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES := $(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES := $(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := $(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) $(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list $(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars $(full_classes_compiled_jar): \ $(java_source_list_file) \ $(java_sources_deps) \ $(full_java_libs) \ $(full_java_bootclasspath_libs) \ $(annotation_processor_deps) \ $(NORMALIZE_PATH) \ $(ZIPTIME) \ $(JAR_ARGS) \ $(ZIPSYNC) \ $(SOONG_ZIP) \ | $(SOONG_JAVAC_WRAPPER) $(transform-host-java-to-package) $(remove-timestamps-from-package) javac-check : $(full_classes_compiled_jar) javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar) .PHONY: javac-check-$(LOCAL_MODULE) $(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF) $(full_classes_combined_jar): $(full_classes_compiled_jar) \ $(jar_manifest_file) \ $(full_static_java_libs) | $(MERGE_ZIPS) $(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \ $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \ $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES) # Run jarjar if necessary, otherwise just copy the file. ifneq ($(strip $(LOCAL_JARJAR_RULES)),) $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) $(full_classes_jarjar_jar): $(full_classes_combined_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) $(call transform-jarjar) else full_classes_jarjar_jar := $(full_classes_combined_jar) endif ####################################### LOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jarjar_jar) include $(BUILD_SYSTEM)/jacoco.mk ####################################### $(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(LOCAL_BUILT_MODULE))) $(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_jar))) ifeq ($(TURBINE_ENABLED),false) $(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_header_jar))) endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_JAVA_LIBRARY)) ================================================ FILE: core/host_java_library_common.mk ================================================ # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Common rules for building a host java library. # LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX) LOCAL_IS_HOST_MODULE := true LOCAL_BUILT_MODULE_STEM := javalib.jar intermediates := $(call local-intermediates-dir) intermediates.COMMON := $(call local-intermediates-dir,COMMON) # base_rules.mk looks at this all_res_assets := proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) ifneq ($(proto_sources),) ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) LOCAL_JAVA_LIBRARIES += libprotobuf-java-micro else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) LOCAL_JAVA_LIBRARIES += libprotobuf-java-nano else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),full) LOCAL_JAVA_LIBRARIES += libprotobuf-java-full else LOCAL_JAVA_LIBRARIES += libprotobuf-java-lite endif endif endif endif LOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES)) ================================================ FILE: core/host_prebuilt.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call record-module-type,HOST_PREBUILT) LOCAL_IS_HOST_MODULE := true include $(BUILD_MULTI_PREBUILT) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_PREBUILT)) ================================================ FILE: core/host_shared_library.mk ================================================ $(call record-module-type,HOST_SHARED_LIBRARY) LOCAL_IS_HOST_MODULE := true my_prefix := HOST_ LOCAL_HOST_PREFIX := include $(BUILD_SYSTEM)/multilib.mk ifndef LOCAL_MODULE_HOST_ARCH ifndef my_module_multilib # libraries default to building for both architecturess my_module_multilib := both endif endif LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/host_shared_library_internal.mk endif ifdef HOST_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for HOST_2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/host_shared_library_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # HOST_2ND_ARCH my_module_arch_supported := ########################################################### ## Copy headers to the install tree ########################################################### ifdef LOCAL_COPY_HEADERS $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) include $(BUILD_SYSTEM)/copy_headers.mk endif ================================================ FILE: core/host_shared_library_internal.mk ================================================ ########################################################### ## Standard rules for building a normal shared library. ## ## Additional inputs from base_rules.make: ## None. ## ## LOCAL_MODULE_SUFFIX will be set for you. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := SHARED_LIBRARIES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := $($(my_prefix)SHLIB_SUFFIX) endif ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) $(error $(LOCAL_PATH): Cannot set module stem for a library) endif ifdef host-shared-library-hook $(call host-shared-library-hook) endif skip_build_from_source := ifdef LOCAL_PREBUILT_MODULE_FILE ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) include $(BUILD_SYSTEM)/prebuilt_internal.mk skip_build_from_source := true endif endif ifndef skip_build_from_source include $(BUILD_SYSTEM)/binary.mk my_host_libprofile_rt := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT) $(LOCAL_BUILT_MODULE): PRIVATE_HOST_LIBPROFILE_RT := $(my_host_libprofile_rt) ifdef USE_HOST_MUSL my_crtbegin := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtbegin_so) my_crtend := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)HOST_OBJECT_libc_musl_crtend_so) my_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) endif $(LOCAL_BUILT_MODULE): PRIVATE_CRTBEGIN := $(my_crtbegin) $(LOCAL_BUILT_MODULE): PRIVATE_CRTEND := $(my_crtend) $(LOCAL_BUILT_MODULE): PRIVATE_LIBCRT_BUILTINS := $(my_libcrt_builtins) $(LOCAL_BUILT_MODULE): $(my_crtbegin) $(my_crtend) $(my_libcrt_builtins) $(LOCAL_BUILT_MODULE): \ $(all_objects) \ $(all_libraries) \ $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-host-o-to-shared-lib) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_SHARED_LIBRARY)) endif # skip_build_from_source ================================================ FILE: core/host_static_library.mk ================================================ $(call record-module-type,HOST_STATIC_LIBRARY) LOCAL_IS_HOST_MODULE := true my_prefix := HOST_ LOCAL_HOST_PREFIX := include $(BUILD_SYSTEM)/multilib.mk ifndef LOCAL_MODULE_HOST_ARCH ifndef my_module_multilib # libraries default to building for both architecturess my_module_multilib := both endif endif LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/host_static_library_internal.mk endif ifdef HOST_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(HOST_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for HOST_2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/host_static_library_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # HOST_2ND_ARCH my_module_arch_supported := ########################################################### ## Copy headers to the install tree ########################################################### ifdef LOCAL_COPY_HEADERS $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) include $(BUILD_SYSTEM)/copy_headers.mk endif ================================================ FILE: core/host_static_library_internal.mk ================================================ ########################################################### ## Standard rules for building a static library for the host. ## ## Additional inputs from base_rules.make: ## None. ## ## LOCAL_MODULE_SUFFIX will be set for you. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := STATIC_LIBRARIES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := .a endif ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) $(error $(LOCAL_PATH): Cannot set module stem for a library) endif LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_SYSTEM)/binary.mk $(LOCAL_BUILT_MODULE): $(built_whole_libraries) $(LOCAL_BUILT_MODULE): $(all_objects) $(transform-host-o-to-static-lib) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=HOST_STATIC_LIBRARY)) ================================================ FILE: core/install_jni_libs.mk ================================================ # Decides how to install the jni libraries needed by an apk. # Input variables: # my_module_multilib, LOCAL_2ND_ARCH_VAR_PREFIX (from package.mk or prebuilt.mk) # rs_compatibility_jni_libs (from java.mk) # my_module_path (from base_rules.mk) # partition_tag (from base_rules.mk) # my_prebuilt_src_file (from prebuilt_internal.mk) # # Output variables: # jni_shared_libraries, jni_shared_libraries_abi, jni_shared_libraries_with_abis if we are going to embed the libraries into the apk; # embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk. # my_embed_jni := ifneq ($(TARGET_BUILD_APPS),) my_embed_jni := true endif ifneq ($(filter tests samples, $(LOCAL_MODULE_TAGS)),) my_embed_jni := true endif # If the APK is not installed in one of the following partitions, force its libraries # to be embedded inside the APK instead of installed to //lib[64]/. supported_partition_patterns := \ $(TARGET_OUT)/% \ $(TARGET_OUT_VENDOR)/% \ $(TARGET_OUT_OEM)/% \ $(TARGET_OUT_PRODUCT)/% \ $(TARGET_OUT_SYSTEM_EXT)/% \ ifeq ($(filter $(supported_partition_patterns),$(my_module_path)),) my_embed_jni := true endif # If we're installing this APP as a compressed module, we include all JNI libraries # in the compressed artifact, rather than as separate files on the partition in question. ifdef LOCAL_COMPRESSED_MODULE my_embed_jni := true endif jni_shared_libraries := jni_shared_libraries_abis := # jni_shared_libraries_with_abis is a list of : jni_shared_libraries_with_abis := embedded_prebuilt_jni_libs := ####################################### # For TARGET_ARCH my_2nd_arch_prefix := my_add_jni := # The module is built for TARGET_ARCH ifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX)) my_add_jni := true endif # Or it explicitly requires both ifeq ($(my_module_multilib),both) my_add_jni := true endif ifeq ($(my_add_jni),true) my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_ARCH)) ifndef my_prebuilt_jni_libs my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS) endif include $(BUILD_SYSTEM)/install_jni_libs_internal.mk jni_shared_libraries += $(my_jni_shared_libraries) jni_shared_libraries_abis += $(my_jni_shared_libraries_abi) jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ $(my_jni_shared_libraries)) embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs) # Include RS dynamically-generated libraries as well # TODO: Add multilib support once RS supports generating multilib libraries. jni_shared_libraries += $(rs_compatibility_jni_libs) jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ $(rs_compatibility_jni_libs)) endif # my_add_jni ####################################### # For TARGET_2ND_ARCH ifdef TARGET_2ND_ARCH my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) my_add_jni := # The module is built for TARGET_2ND_ARCH ifeq ($(my_2nd_arch_prefix),$(LOCAL_2ND_ARCH_VAR_PREFIX)) my_add_jni := true endif # Or it explicitly requires both ifeq ($(my_module_multilib),both) my_add_jni := true endif ifeq ($(my_add_jni),true) my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS_$(TARGET_2ND_ARCH)) ifndef my_prebuilt_jni_libs my_prebuilt_jni_libs := $(LOCAL_PREBUILT_JNI_LIBS) endif include $(BUILD_SYSTEM)/install_jni_libs_internal.mk jni_shared_libraries += $(my_jni_shared_libraries) jni_shared_libraries_abis += $(my_jni_shared_libraries_abi) jni_shared_libraries_with_abis += $(addprefix $(my_jni_shared_libraries_abi):,\ $(my_jni_shared_libraries)) embedded_prebuilt_jni_libs += $(my_embedded_prebuilt_jni_libs) endif # my_add_jni endif # TARGET_2ND_ARCH jni_shared_libraries := $(strip $(jni_shared_libraries)) jni_shared_libraries_abis := $(sort $(jni_shared_libraries_abis)) jni_shared_libraries_with_abis := $(strip $(jni_shared_libraries_with_abis)) embedded_prebuilt_jni_libs := $(strip $(embedded_prebuilt_jni_libs)) ================================================ FILE: core/install_jni_libs_internal.mk ================================================ # Install jni libraries for one arch. # Input variables: # my_2nd_arch_prefix: indicate if this is for TARGET_2ND_ARCH. # my_embed_jni: indicate if we want to embed the jni libs in the apk. # my_prebuilt_jni_libs # my_installed_module_stem (from configure_module_stem.mk) # partition_tag (from base_rules.mk) # partition_lib_pairs # my_prebuilt_src_file (from prebuilt_internal.mk) # # Output variables: # my_jni_shared_libraries, my_jni_shared_libraries_abi, if we are going to embed the libraries into the apk; # my_embedded_prebuilt_jni_libs, prebuilt jni libs embedded in prebuilt apk. # my_sdk_variant = $(1) ifneq (,$(and $(my_embed_jni),$(LOCAL_SDK_VERSION))) # Soong produces $(lib).so in $(lib).sdk_intermediates so that the library # has the correct name for embedding in an APK. Append .sdk to the name # of the intermediates directory, but not the .so name. my_sdk_variant = $(call use_soong_sdk_libraries,$(1)) endif my_jni_shared_libraries := $(strip \ $(foreach lib,$(LOCAL_JNI_SHARED_LIBRARIES), \ $(call intermediates-dir-for,SHARED_LIBRARIES,$(call my_sdk_variant,$(lib)),,,$(my_2nd_arch_prefix))/$(lib).so)) # App-specific lib path. my_app_lib_path := $(dir $(LOCAL_INSTALLED_MODULE))lib/$(TARGET_$(my_2nd_arch_prefix)ARCH) my_embedded_prebuilt_jni_libs := ifdef my_embed_jni # App explicitly requires the prebuilt NDK stl shared libraies. # The NDK stl shared libraries should never go to the system image. ifeq ($(LOCAL_NDK_STL_VARIANT),c++_shared) ifndef LOCAL_SDK_VERSION $(error LOCAL_SDK_VERSION must be defined with LOCAL_NDK_STL_VARIANT, \ LOCAL_PACKAGE_NAME=$(LOCAL_PACKAGE_NAME)) endif my_libcxx_arch := $($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR) my_jni_shared_libraries += \ $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/android_libc++/ndk/$(my_libcxx_arch)/lib/libc++_shared.so endif # Set the abi directory used by the local JNI shared libraries. # (Doesn't change how the local shared libraries are compiled, just # sets where they are stored in the apk.) ifeq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI),) my_jni_shared_libraries_abi := $(TARGET_$(my_2nd_arch_prefix)CPU_ABI) else my_jni_shared_libraries_abi := $(LOCAL_JNI_SHARED_LIBRARIES_ABI) endif else ifneq ($(my_jni_shared_libraries),) # not my_embed_jni # The jni libaries will be installed to the system.img. my_jni_filenames := $(notdir $(my_jni_shared_libraries)) # Make sure the JNI libraries get installed my_shared_library_path := $(call get_non_asan_path,\ $($(my_2nd_arch_prefix)TARGET_OUT$(partition_tag)_SHARED_LIBRARIES)) bit_suffix := $(if $(filter %64,$(TARGET_$(my_2nd_arch_prefix)ARCH)),:64,:32) ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += $(addsuffix $(bit_suffix),$(LOCAL_JNI_SHARED_LIBRARIES)) # Create symlink in the app specific lib path # Skip creating this symlink when running the second part of a target sanitization build. ifeq ($(filter address,$(SANITIZE_TARGET)),) my_symlink_target_dir := $(patsubst $(PRODUCT_OUT)%,%,\ $(my_shared_library_path)) ifdef partition_lib_pairs # Support cross-partition jni lib dependency for bp modules # API domain check is done in Soong $(foreach pl_pair,$(partition_lib_pairs),\ $(eval lib_name := $(call word-colon, 1, $(pl_pair)))\ $(eval lib_partition := $(call word-colon, 2, $(pl_pair)))\ $(eval shared_library_path := $(call get_non_asan_path,\ $($(my_2nd_arch_prefix)TARGET_OUT$(lib_partition)_SHARED_LIBRARIES)))\ $(call symlink-file,\ $(shared_library_path)/$(lib_name).so,\ $(my_symlink_target_dir)/$(lib_name).so,\ $(my_app_lib_path)/$(lib_name).so)\ $(eval $$(LOCAL_INSTALLED_MODULE) : $$(my_app_lib_path)/$$(lib_name).so)\ $(eval ALL_MODULES.$(my_register_name).INSTALLED += $$(my_app_lib_path)/$$(lib_name).so)) else # Cross-partition jni lib dependency currently not supported for mk modules $(foreach lib,$(my_jni_filenames),\ $(call symlink-file, \ $(my_shared_library_path)/$(lib), \ $(my_symlink_target_dir)/$(lib), \ $(my_app_lib_path)/$(lib)) \ $(eval $$(LOCAL_INSTALLED_MODULE) : $$(my_app_lib_path)/$$(lib)) \ $(eval ALL_MODULES.$(my_register_name).INSTALLED += $$(my_app_lib_path)/$$(lib))) endif # partition_lib_pairs endif # Clear jni_shared_libraries to not embed it into the apk. my_jni_shared_libraries := endif # my_embed_jni ifdef my_prebuilt_jni_libs # Files like @lib//libfoo.so (path inside the apk) are JNI libs embedded prebuilt apk; # Files like path/to/libfoo.so (path relative to LOCAL_PATH) are prebuilts in the source tree. my_embedded_prebuilt_jni_libs := $(patsubst @%,%, \ $(filter @%, $(my_prebuilt_jni_libs))) # prebuilt JNI exsiting as separate source files. my_prebuilt_jni_libs := $(addprefix $(LOCAL_PATH)/, \ $(filter-out @%, $(my_prebuilt_jni_libs))) ifdef my_prebuilt_jni_libs ifdef my_embed_jni # Embed my_prebuilt_jni_libs to the apk my_jni_shared_libraries += $(my_prebuilt_jni_libs) else # not my_embed_jni # Install my_prebuilt_jni_libs as separate files. $(foreach lib, $(my_prebuilt_jni_libs), \ $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib))))) my_installed_library := $(addprefix $(my_app_lib_path)/, $(notdir $(my_prebuilt_jni_libs))) $(LOCAL_INSTALLED_MODULE) : $(my_installed_library) ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed_library) endif # my_embed_jni endif # inner my_prebuilt_jni_libs endif # outer my_prebuilt_jni_libs # Verify that all included libraries are built against the NDK include $(BUILD_SYSTEM)/allowed_ndk_types.mk ifneq ($(strip $(LOCAL_JNI_SHARED_LIBRARIES)),) ifneq ($(LOCAL_SDK_VERSION),) my_link_type := app:sdk my_warn_types := native:platform $(my_warn_ndk_types) my_allowed_types := $(my_allowed_ndk_types) ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) my_allowed_types += native:vendor native:vndk native:platform_vndk else ifeq ($(LOCAL_PRODUCT_MODULE),true) my_allowed_types += native:product native:vndk native:platform_vndk endif else my_link_type := app:platform my_warn_types := $(my_warn_ndk_types) my_allowed_types := $(my_allowed_ndk_types) native:platform native:product native:vendor native:vndk native:vndk_private native:platform_vndk endif ifeq ($(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)) # SOONG_SDK_VARIANT_MODULES isn't complete yet while parsing Soong modules, and Soong has # already ensured that apps link against the correct SDK variants, don't check them. else ifneq (,$(LOCAL_SDK_VERSION)) my_link_deps := $(addprefix SHARED_LIBRARIES:,$(call use_soong_sdk_libraries,$(LOCAL_JNI_SHARED_LIBRARIES))) else my_link_deps := $(addprefix SHARED_LIBRARIES:,$(LOCAL_JNI_SHARED_LIBRARIES)) endif endif my_common := include $(BUILD_SYSTEM)/link_type.mk endif ================================================ FILE: core/instrumentation_test_config_template.xml ================================================ ================================================ FILE: core/jacoco.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file sets up Java code coverage via Jacoco # This file is only intended to be included internally by the build system # (at the time of authorship, it is included by java.mk and # java_host_library.mk) # determine Jacoco include/exclude filters even when coverage is not enabled # to get syntax checking on LOCAL_JACK_COVERAGE_(INCLUDE|EXCLUDE)_FILTER # copy filters from Jack but also skip some known java packages my_include_filter := $(strip $(LOCAL_JACK_COVERAGE_INCLUDE_FILTER)) my_exclude_filter := $(strip $(DEFAULT_JACOCO_EXCLUDE_FILTER),$(LOCAL_JACK_COVERAGE_EXCLUDE_FILTER)) my_include_args := $(call jacoco-class-filter-to-file-args, $(my_include_filter)) my_exclude_args := $(call jacoco-class-filter-to-file-args, $(my_exclude_filter)) # single-quote each arg of the include args so the '*' gets evaluated by zip # don't quote the exclude args they need to be evaluated by bash for rm -rf my_include_args := $(foreach arg,$(my_include_args),'$(arg)') ifeq ($(LOCAL_EMMA_INSTRUMENT),true) my_files := $(intermediates.COMMON)/jacoco # make a task that unzips the classes that we want to instrument from the # input jar my_unzipped_path := $(my_files)/work/classes-to-instrument/classes my_unzipped_timestamp_path := $(my_files)/work/classes-to-instrument/updated.stamp $(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) $(my_unzipped_timestamp_path): PRIVATE_UNZIPPED_TIMESTAMP_PATH := $(my_unzipped_timestamp_path) $(my_unzipped_timestamp_path): PRIVATE_INCLUDE_ARGS := $(my_include_args) $(my_unzipped_timestamp_path): PRIVATE_EXCLUDE_ARGS := $(my_exclude_args) $(my_unzipped_timestamp_path): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) $(my_unzipped_timestamp_path): $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) rm -rf $(PRIVATE_UNZIPPED_PATH) $@ mkdir -p $(PRIVATE_UNZIPPED_PATH) unzip -qDD $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) \ -d $(PRIVATE_UNZIPPED_PATH) \ $(PRIVATE_INCLUDE_ARGS) chmod -R =rwX $(PRIVATE_UNZIPPED_PATH) (cd $(PRIVATE_UNZIPPED_PATH) && rm -rf $(PRIVATE_EXCLUDE_ARGS)) (cd $(PRIVATE_UNZIPPED_PATH) && find -not -name "*.class" -type f -exec rm {} \;) touch $(PRIVATE_UNZIPPED_TIMESTAMP_PATH) # Unfortunately in the previous task above, # 'rm -rf $(PRIVATE_EXCLUDE_ARGS)' needs to be a separate # shell command after 'unzip'. # We can't just use the '-x' (exclude) option of 'unzip' because if both # inclusions and exclusions are specified and an exclusion matches no # inclusions, then 'unzip' exits with an error (error 11). # We could ignore the error, but that would make the process less reliable # make a task that zips only the classes that will be instrumented # (for passing in to the report generator later) my_classes_to_report_on_path := $(my_files)/report-resources/jacoco-report-classes.jar $(my_classes_to_report_on_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) $(my_classes_to_report_on_path): $(my_unzipped_timestamp_path) rm -f $@ zip -q $@ \ -r $(PRIVATE_UNZIPPED_PATH) # Make a rule to copy the jacoco-report-classes.jar to a packaging directory. $(eval $(call copy-one-file,$(my_classes_to_report_on_path),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) $(call add-dependency,$(LOCAL_BUILT_MODULE),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) # make a task that invokes instrumentation my_instrumented_path := $(my_files)/work/instrumented/classes my_instrumented_timestamp_path := $(my_files)/work/instrumented/updated.stamp $(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path) $(my_instrumented_timestamp_path): PRIVATE_INSTRUMENTED_TIMESTAMP_PATH := $(my_instrumented_timestamp_path) $(my_instrumented_timestamp_path): PRIVATE_UNZIPPED_PATH := $(my_unzipped_path) $(my_instrumented_timestamp_path): $(my_unzipped_timestamp_path) $(JACOCO_CLI_JAR) rm -rf $(PRIVATE_INSTRUMENTED_PATH) mkdir -p $(PRIVATE_INSTRUMENTED_PATH) java -jar $(JACOCO_CLI_JAR) \ instrument \ --quiet \ --dest '$(PRIVATE_INSTRUMENTED_PATH)' \ $(PRIVATE_UNZIPPED_PATH) touch $(PRIVATE_INSTRUMENTED_TIMESTAMP_PATH) # make a task that zips both the instrumented classes and the uninstrumented # classes (this jar is the instrumented application to execute) my_temp_jar_path := $(my_files)/work/usable.jar LOCAL_FULL_CLASSES_JACOCO_JAR := $(intermediates.COMMON)/classes-jacoco.jar $(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_TEMP_JAR_PATH := $(my_temp_jar_path) $(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_INSTRUMENTED_PATH := $(my_instrumented_path) $(LOCAL_FULL_CLASSES_JACOCO_JAR): PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) $(LOCAL_FULL_CLASSES_JACOCO_JAR): $(JAR_ARGS) $(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_instrumented_timestamp_path) $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) rm -f $@ $(PRIVATE_TEMP_JAR_PATH) # copy the pre-jacoco jar (containing files excluded from instrumentation) cp $(PRIVATE_FULL_CLASSES_PRE_JACOCO_JAR) $(PRIVATE_TEMP_JAR_PATH) # copy instrumented files back into the resultant jar $(JAR) -uf $(PRIVATE_TEMP_JAR_PATH) $(call jar-args-sorted-files-in-directory,$(PRIVATE_INSTRUMENTED_PATH)) mv $(PRIVATE_TEMP_JAR_PATH) $@ # this is used to trigger $(my_classes_to_report_on_path) to build # when $(LOCAL_FULL_CLASSES_JACOCO_JAR) builds, but it isn't truly a # dependency. $(LOCAL_FULL_CLASSES_JACOCO_JAR): $(my_classes_to_report_on_path) else # LOCAL_EMMA_INSTRUMENT != true LOCAL_FULL_CLASSES_JACOCO_JAR := $(LOCAL_FULL_CLASSES_PRE_JACOCO_JAR) endif # LOCAL_EMMA_INSTRUMENT == true LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_FULL_CLASSES_JACOCO_JAR) ================================================ FILE: core/java.mk ================================================ # Target Java. # Requires: # LOCAL_MODULE_SUFFIX # LOCAL_MODULE_CLASS # all_res_assets LOCAL_NO_STANDARD_LIBRARIES:=$(strip $(LOCAL_NO_STANDARD_LIBRARIES)) LOCAL_SDK_VERSION:=$(strip $(LOCAL_SDK_VERSION)) proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) ifneq ($(proto_sources),) ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-micro else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-nano else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream) # No library for stream protobufs else LOCAL_STATIC_JAVA_LIBRARIES += libprotobuf-java-lite endif endif endif endif # LOCAL_STATIC_JAVA_AAR_LIBRARIES and LOCAL_STATIC_ANDROID_LIBRARIES are also LOCAL_STATIC_JAVA_LIBRARIES. LOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(LOCAL_STATIC_JAVA_AAR_LIBRARIES) \ $(LOCAL_STATIC_ANDROID_LIBRARIES)) # LOCAL_SHARED_ANDROID_LIBRARIES are also LOCAL_JAVA_LIBRARIES. LOCAL_JAVA_LIBRARIES := $(sort $(LOCAL_JAVA_LIBRARIES) $(LOCAL_SHARED_ANDROID_LIBRARIES)) LOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM)) ifeq ($(LOCAL_BUILT_MODULE_STEM),) $(error $(LOCAL_PATH): Target java template must define LOCAL_BUILT_MODULE_STEM) endif ifneq ($(filter classes-compiled.jar classes.jar,$(LOCAL_BUILT_MODULE_STEM)),) $(error LOCAL_BUILT_MODULE_STEM may not be "$(LOCAL_BUILT_MODULE_STEM)") endif ############################################################################## # Define the intermediate targets before including base_rules so they get # the correct environment. ############################################################################## intermediates := $(call local-intermediates-dir) intermediates.COMMON := $(call local-intermediates-dir,COMMON) ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) LOCAL_PROGUARD_ENABLED := endif full_classes_turbine_jar := $(intermediates.COMMON)/classes-turbine.jar full_classes_header_jarjar := $(intermediates.COMMON)/classes-header-jarjar.jar full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar full_classes_processed_jar := $(intermediates.COMMON)/classes-processed.jar full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar full_classes_combined_jar := $(intermediates.COMMON)/classes-combined.jar built_dex_intermediate := $(intermediates.COMMON)/dex/classes.dex full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar java_source_list_file := $(intermediates.COMMON)/java-source-list ifeq ($(LOCAL_MODULE_CLASS)$(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),APPS) # If this is an apk without any Java code (e.g. framework-res), we should skip compiling Java. full_classes_jar := built_dex := else full_classes_jar := $(intermediates.COMMON)/classes.jar built_dex := $(intermediates.COMMON)/classes.dex endif LOCAL_INTERMEDIATE_TARGETS += \ $(full_classes_turbine_jar) \ $(full_classes_compiled_jar) \ $(full_classes_jarjar_jar) \ $(full_classes_jar) \ $(full_classes_combined_jar) \ $(built_dex_intermediate) \ $(built_dex) \ $(full_classes_stubs_jar) \ $(java_source_list_file) ########################################################### ## AIDL: Compile .aidl files to .java ########################################################### aidl_sources := $(filter %.aidl,$(LOCAL_SRC_FILES)) aidl_java_sources := ifneq ($(strip $(aidl_sources)),) aidl_preprocess_import := ifdef LOCAL_SDK_VERSION ifneq ($(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS)),) # LOCAL_SDK_VERSION is current and no TARGET_BUILD_USE_PREBUILT_SDKS aidl_preprocess_import := $(FRAMEWORK_AIDL) else aidl_preprocess_import := $(call resolve-prebuilt-sdk-aidl-path,$(LOCAL_SDK_VERSION)) endif # not current or system_current else # build against the platform. LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) endif # LOCAL_SDK_VERSION $(foreach s,$(aidl_sources),\ $(eval $(call define-aidl-java-rule,$(s),$(intermediates.COMMON)/aidl,aidl_java_sources))) $(foreach java,$(aidl_java_sources), \ $(call include-depfile,$(java:%.java=%.P),$(java))) $(aidl_java_sources) : $(LOCAL_ADDITIONAL_DEPENDENCIES) $(aidl_preprocess_import) $(aidl_java_sources): PRIVATE_AIDL_FLAGS := $(addprefix -p,$(aidl_preprocess_import)) -I$(LOCAL_PATH) -I$(LOCAL_PATH)/src $(addprefix -I,$(LOCAL_AIDL_INCLUDES)) $(aidl_java_sources): PRIVATE_MODULE := $(LOCAL_MODULE) endif ########################################## # All of the rules after full_classes_compiled_jar are very unlikely # to fail except for bugs in their respective tools. If you would # like to run these rules, add the "all" modifier goal to the make # command line. ifndef LOCAL_CHECKED_MODULE ifdef full_classes_jar LOCAL_CHECKED_MODULE := $(full_classes_compiled_jar) endif endif ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### ########################################################### ## logtags: emit java source ########################################################### ifneq ($(strip $(logtags_sources)),) logtags_java_sources := $(patsubst %.logtags,%.java,$(addprefix $(intermediates.COMMON)/logtags/, $(logtags_sources))) logtags_sources := $(addprefix $(LOCAL_PATH)/, $(logtags_sources)) $(logtags_java_sources): $(intermediates.COMMON)/logtags/%.java: $(LOCAL_PATH)/%.logtags $(JAVATAGS) $(transform-logtags-to-java) else logtags_java_sources := endif ########################################## java_sources := $(addprefix $(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) $(aidl_java_sources) $(logtags_java_sources) \ $(filter %.java,$(LOCAL_GENERATED_SOURCES)) java_intermediate_sources := $(addprefix $(TARGET_OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES))) all_java_sources := $(java_sources) $(java_intermediate_sources) ALL_MODULES.$(my_register_name).SRCS := $(ALL_MODULES.$(my_register_name).SRCS) $(all_java_sources) include $(BUILD_SYSTEM)/java_common.mk include $(BUILD_SYSTEM)/sdk_check.mk # Set the profile source so that the odex / profile code included from java.mk # can find it. # # TODO: b/64896089, this is broken when called from package_internal.mk, since the file # we preopt from is a temporary file. This will be addressed in a follow up, possibly # by disabling stripping for profile guided preopt (which may be desirable for other # reasons anyway). # # Note that we set this only when called from package_internal.mk and not in other cases. ifneq (,$(called_from_package_internal) dex_preopt_profile_src_file := $(LOCAL_BUILT_MODULE) endif ####################################### # defines built_odex along with rule to install odex my_manifest_or_apk := $(full_android_manifest) include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk my_manifest_or_apk := ####################################### # Make sure there's something to build. ifdef full_classes_jar ifndef need_compile_java $(call pretty-error,Target java module does not define any source or resource files) endif endif # Since we're using intermediates.COMMON, make sure that it gets cleaned # properly. $(cleantarget): PRIVATE_CLEAN_FILES += $(intermediates.COMMON) ifdef full_classes_jar # Droiddoc isn't currently able to generate stubs for modules, so we're just # allowing it to use the classes.jar as the "stubs" that would be use to link # against, for the cases where someone needs the jar to link against. $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_stubs_jar))) ALL_MODULES.$(my_register_name).STUBS := $(full_classes_stubs_jar) $(full_classes_compiled_jar): PRIVATE_WARNINGS_ENABLE := $(LOCAL_WARNINGS_ENABLE) # Compile the java files to a .jar file. # This intentionally depends on java_sources, not all_java_sources. # Deps for generated source files must be handled separately, # via deps on the target that generates the sources. # For user / userdebug builds, strip the local variable table and the local variable # type table. This has no bearing on stack traces, but will leave less information # available via JDWP. ifneq (,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO)) ifneq (,$(filter userdebug user,$(TARGET_BUILD_VARIANT))) LOCAL_JAVACFLAGS+= -g:source,lines endif endif # List of dependencies for anything that needs all java sources in place java_sources_deps := \ $(java_sources) \ $(java_resource_sources) \ $(LOCAL_SRCJARS) \ $(LOCAL_ADDITIONAL_DEPENDENCIES) $(java_source_list_file): $(java_sources_deps) $(NORMALIZE_PATH) $(write-java-source-list) ALL_MODULES.$(my_register_name).SRCJARS := $(LOCAL_SRCJARS) ifneq ($(TURBINE_ENABLED),false) $(full_classes_turbine_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) $(full_classes_turbine_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) $(full_classes_turbine_jar): \ $(java_source_list_file) \ $(java_sources_deps) \ $(full_java_header_libs) \ $(full_java_bootclasspath_libs) \ $(full_java_system_modules_deps) \ $(NORMALIZE_PATH) \ $(JAR_ARGS) \ $(ZIPTIME) \ | $(TURBINE) \ $(MERGE_ZIPS) $(transform-java-to-header.jar) .KATI_RESTAT: $(full_classes_turbine_jar) # Run jarjar before generate classes-header.jar if necessary. ifneq ($(strip $(LOCAL_JARJAR_RULES)),) $(full_classes_header_jarjar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) $(full_classes_header_jarjar): $(full_classes_turbine_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) $(call transform-jarjar) else full_classes_header_jarjar := $(full_classes_turbine_jar) endif $(eval $(call copy-one-file,$(full_classes_header_jarjar),$(full_classes_header_jar))) endif # TURBINE_ENABLED != false # TODO(b/143658984): goma can't handle the --system argument to javac. #$(full_classes_compiled_jar): .KATI_NINJA_POOL := $(GOMA_POOL) $(full_classes_compiled_jar): .KATI_NINJA_POOL := $(JAVAC_NINJA_POOL) $(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS) $(annotation_processor_flags) $(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES := $(LOCAL_JAR_EXCLUDE_FILES) $(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES := $(LOCAL_JAR_PACKAGES) $(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_PACKAGES := $(LOCAL_JAR_EXCLUDE_PACKAGES) $(full_classes_compiled_jar): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file) $(full_classes_compiled_jar): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs) $(full_classes_compiled_jar): PRIVATE_SRCJARS := $(LOCAL_SRCJARS) $(full_classes_compiled_jar): PRIVATE_SRCJAR_LIST_FILE := $(intermediates.COMMON)/srcjar-list $(full_classes_compiled_jar): PRIVATE_SRCJAR_INTERMEDIATES_DIR := $(intermediates.COMMON)/srcjars $(full_classes_compiled_jar): \ $(java_source_list_file) \ $(full_java_header_libs) \ $(java_sources_deps) \ $(full_java_bootclasspath_libs) \ $(full_java_system_modules_deps) \ $(layers_file) \ $(annotation_processor_deps) \ $(NORMALIZE_PATH) \ $(JAR_ARGS) \ $(ZIPSYNC) \ $(SOONG_ZIP) \ | $(SOONG_JAVAC_WRAPPER) @echo "Target Java: $@ $(call compile-java,$(TARGET_JAVAC),$(PRIVATE_ALL_JAVA_HEADER_LIBRARIES)) javac-check : $(full_classes_compiled_jar) javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar) .PHONY: javac-check-$(LOCAL_MODULE) $(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF) $(full_classes_combined_jar): $(full_classes_compiled_jar) \ $(jar_manifest_file) \ $(full_static_java_libs) | $(MERGE_ZIPS) $(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \ $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \ $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES) ifdef LOCAL_JAR_PROCESSOR # LOCAL_JAR_PROCESSOR_ARGS must be evaluated here to set up the rule-local # PRIVATE_JAR_PROCESSOR_ARGS variable, but $< and $@ are not available yet. # Set ${in} and ${out} so they can be referenced by LOCAL_JAR_PROCESSOR_ARGS # using deferred evaluation (LOCAL_JAR_PROCESSOR_ARGS = instead of :=). in := $(full_classes_combined_jar) out := $(full_classes_processed_jar).tmp my_jar_processor := $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_JAR_PROCESSOR).jar $(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR_ARGS := $(LOCAL_JAR_PROCESSOR_ARGS) $(full_classes_processed_jar): PRIVATE_JAR_PROCESSOR := $(my_jar_processor) $(full_classes_processed_jar): PRIVATE_TMP_OUT := $(out) in := out := $(full_classes_processed_jar): $(full_classes_combined_jar) $(my_jar_processor) @echo Processing $@ with $(PRIVATE_JAR_PROCESSOR) $(hide) rm -f $@ $(PRIVATE_TMP_OUT) $(hide) $(JAVA) -jar $(PRIVATE_JAR_PROCESSOR) $(PRIVATE_JAR_PROCESSOR_ARGS) $(hide) mv $(PRIVATE_TMP_OUT) $@ my_jar_processor := else full_classes_processed_jar := $(full_classes_combined_jar) endif # Run jarjar if necessary ifneq ($(strip $(LOCAL_JARJAR_RULES)),) $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) $(full_classes_jarjar_jar): $(full_classes_processed_jar) $(LOCAL_JARJAR_RULES) | $(JARJAR) $(call transform-jarjar) else full_classes_jarjar_jar := $(full_classes_processed_jar) endif $(eval $(call copy-one-file,$(full_classes_jarjar_jar),$(full_classes_jar))) ####################################### LOCAL_FULL_CLASSES_PRE_JACOCO_JAR := $(full_classes_jar) include $(BUILD_SYSTEM)/jacoco.mk ####################################### # Temporarily enable --multi-dex until proguard supports v53 class files # ( http://b/67673860 ) or we move away from proguard altogether. LOCAL_DX_FLAGS := $(filter-out --multi-dex,$(LOCAL_DX_FLAGS)) --multi-dex full_classes_pre_proguard_jar := $(LOCAL_FULL_CLASSES_JACOCO_JAR) # Keep a copy of the jar just before proguard processing. $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(intermediates.COMMON)/classes-pre-proguard.jar)) # Run proguard if necessary ifdef LOCAL_PROGUARD_ENABLED ifneq ($(filter-out full custom obfuscation optimization,$(LOCAL_PROGUARD_ENABLED)),) $(warning while processing: $(LOCAL_MODULE)) $(error invalid value for LOCAL_PROGUARD_ENABLED: $(LOCAL_PROGUARD_ENABLED)) endif proguard_dictionary := $(intermediates.COMMON)/proguard_dictionary proguard_configuration := $(intermediates.COMMON)/proguard_configuration # When an app contains references to APIs that are not in the SDK specified by # its LOCAL_SDK_VERSION for example added by support library or by runtime # classes added by desugar, we artifically raise the "SDK version" "linked" by # ProGuard, to # - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version. # - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version. # See b/20667396 my_proguard_sdk_raise := ifdef LOCAL_SDK_VERSION ifdef TARGET_BUILD_APPS ifeq (,$(filter current system_current test_current core_current, $(LOCAL_SDK_VERSION))) my_proguard_sdk_raise := $(call java-lib-header-files, $(call resolve-prebuilt-sdk-module,current)) endif else # For platform build, we can't just raise to the "current" SDK, # that would break apps that use APIs removed from the current SDK. my_proguard_sdk_raise := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES)) endif ifdef BOARD_SYSTEMSDK_VERSIONS ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) # But for vendor or odm apks, don't raise SDK as the apks are required to # use SDK APIs only my_proguard_sdk_raise := endif endif endif legacy_proguard_flags := $(addprefix -libraryjars ,$(my_proguard_sdk_raise) \ $(filter-out $(my_proguard_sdk_raise), \ $(full_java_bootclasspath_libs) \ $(full_shared_java_header_libs))) legacy_proguard_lib_deps := $(my_proguard_sdk_raise) \ $(filter-out $(my_proguard_sdk_raise),$(full_java_bootclasspath_libs) $(full_shared_java_header_libs)) legacy_proguard_flags += -printmapping $(proguard_dictionary) legacy_proguard_flags += -printconfiguration $(proguard_configuration) common_proguard_flags := common_proguard_flag_files := $(BUILD_SYSTEM)/proguard.flags ifneq ($(LOCAL_INSTRUMENTATION_FOR)$(filter tests,$(LOCAL_MODULE_TAGS)),) common_proguard_flags += -dontshrink # don't shrink tests by default endif # test package ifneq ($(LOCAL_PROGUARD_ENABLED),custom) common_proguard_flag_files += $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags) endif ifneq ($(common_proguard_flag_files),) common_proguard_flags += $(addprefix -include , $(common_proguard_flag_files)) # This is included from $(BUILD_SYSTEM)/proguard.flags common_proguard_flag_files += $(BUILD_SYSTEM)/proguard_basic_keeps.flags endif ifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) # By default no obfuscation common_proguard_flags += -dontobfuscate endif # No obfuscation ifeq ($(filter optimization,$(LOCAL_PROGUARD_ENABLED)),) # By default no optimization common_proguard_flags += -dontoptimize endif # No optimization ifdef LOCAL_INSTRUMENTATION_FOR ifeq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) # If no obfuscation, link in the instrmented package's classes.jar as a library. # link_instr_classes_jar is defined in base_rule.mk legacy_proguard_flags += -libraryjars $(link_instr_classes_jar) legacy_proguard_lib_deps += $(link_instr_classes_jar) else # obfuscation # If obfuscation is enabled, the main app must be obfuscated too. # We need to run obfuscation using the main app's dictionary, # and treat the main app's class.jar as injars instead of libraryjars. legacy_proguard_flags := -injars $(link_instr_classes_jar) \ -outjars $(intermediates.COMMON)/proguard.$(LOCAL_INSTRUMENTATION_FOR).jar \ -include $(link_instr_intermediates_dir.COMMON)/proguard_options \ -applymapping $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \ -verbose \ $(legacy_proguard_flags) legacy_proguard_lib_deps += \ $(link_instr_classes_jar) \ $(link_instr_intermediates_dir.COMMON)/proguard_options \ $(link_instr_intermediates_dir.COMMON)/proguard_dictionary \ # Sometimes (test + main app) uses different keep rules from the main app - # apply the main app's dictionary anyway. legacy_proguard_flags += -ignorewarnings endif # no obfuscation endif # LOCAL_INSTRUMENTATION_FOR proguard_flag_files := $(addprefix $(LOCAL_PATH)/, $(LOCAL_PROGUARD_FLAG_FILES)) proguard_flag_files += $(addprefix $(LOCAL_PATH)/, $(LOCAL_R8_FLAG_FILES)) LOCAL_PROGUARD_FLAGS += $(addprefix -include , $(proguard_flag_files)) LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_flag_files) proguard_flag_files := ifdef LOCAL_TEST_MODULE_TO_PROGUARD_WITH extra_input_jar := $(call intermediates-dir-for,APPS,$(LOCAL_TEST_MODULE_TO_PROGUARD_WITH),,COMMON)/classes.jar else extra_input_jar := endif ifneq ($(filter obfuscation,$(LOCAL_PROGUARD_ENABLED)),) $(built_dex_intermediate): .KATI_IMPLICIT_OUTPUTS := $(proguard_dictionary) $(proguard_configuration) # Make a rule to copy the proguard_dictionary to a packaging directory. $(eval $(call copy-one-file,$(proguard_dictionary),\ $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary)) $(call add-dependency,$(LOCAL_BUILT_MODULE),\ $(call local-packaging-dir,proguard_dictionary)/proguard_dictionary) $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),\ $(call local-packaging-dir,proguard_dictionary)/classes.jar)) $(call add-dependency,$(LOCAL_BUILT_MODULE),\ $(call local-packaging-dir,proguard_dictionary)/classes.jar) endif endif # LOCAL_PROGUARD_ENABLED defined ifneq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) $(built_dex_intermediate): PRIVATE_DX_FLAGS := $(LOCAL_DX_FLAGS) ifdef LOCAL_PROGUARD_ENABLED $(built_dex_intermediate): .KATI_NINJA_POOL := $(R8_NINJA_POOL) $(built_dex_intermediate): PRIVATE_EXTRA_INPUT_JAR := $(extra_input_jar) $(built_dex_intermediate): PRIVATE_PROGUARD_FLAGS := $(legacy_proguard_flags) $(common_proguard_flags) $(LOCAL_PROGUARD_FLAGS) $(built_dex_intermediate): PRIVATE_PROGUARD_DICTIONARY := $(proguard_dictionary) $(built_dex_intermediate) : $(full_classes_pre_proguard_jar) $(extra_input_jar) $(my_proguard_sdk_raise) $(common_proguard_flag_files) $(legacy_proguard_lib_deps) $(R8) $(LOCAL_PROGUARD_FLAGS_DEPS) $(transform-jar-to-dex-r8) else # !LOCAL_PROGUARD_ENABLED $(built_dex_intermediate): .KATI_NINJA_POOL := $(D8_NINJA_POOL) $(built_dex_intermediate): PRIVATE_D8_LIBS := $(full_java_bootclasspath_libs) $(full_shared_java_header_libs) $(built_dex_intermediate): $(full_java_bootclasspath_libs) $(full_shared_java_header_libs) $(built_dex_intermediate): $(full_classes_pre_proguard_jar) $(D8) $(ZIP2ZIP) $(transform-classes.jar-to-dex) endif $(foreach pair,$(PRODUCT_BOOT_JARS), \ $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \ $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files))) $(built_dex): $(built_dex_intermediate) @echo Copying: $@ $(hide) mkdir -p $(dir $@) $(hide) rm -f $(dir $@)/classes*.dex $(hide) cp -fp $(dir $<)/classes*.dex $(dir $@) java-dex: $(built_dex) endif # !LOCAL_IS_STATIC_JAVA_LIBRARY findbugs_xml := $(intermediates.COMMON)/findbugs.xml $(findbugs_xml): PRIVATE_AUXCLASSPATH := $(addprefix -auxclasspath ,$(strip \ $(call normalize-path-list,$(filter %.jar,$(full_java_libs))))) $(findbugs_xml): PRIVATE_FINDBUGS_FLAGS := $(LOCAL_FINDBUGS_FLAGS) $(findbugs_xml) : $(full_classes_pre_proguard_jar) $(filter %.xml, $(LOCAL_FINDBUGS_FLAGS)) @echo Findbugs: $@ $(hide) $(FINDBUGS) -textui -effort:min -xml:withMessages \ $(PRIVATE_AUXCLASSPATH) $(PRIVATE_FINDBUGS_FLAGS) \ $< \ > $@ ALL_FINDBUGS_FILES += $(findbugs_xml) findbugs_html := $(PRODUCT_OUT)/findbugs/$(LOCAL_MODULE).html $(findbugs_html) : PRIVATE_XML_FILE := $(findbugs_xml) $(LOCAL_MODULE)-findbugs : $(findbugs_html) .PHONY: $(LOCAL_MODULE)-findbugs $(findbugs_html) : $(findbugs_xml) @mkdir -p $(dir $@) @echo ConvertXmlToText: $@ $(hide) $(FINDBUGS_DIR)/convertXmlToText -html:fancy.xsl $(PRIVATE_XML_FILE) \ > $@ $(LOCAL_MODULE)-findbugs : $(findbugs_html) endif # full_classes_jar is defined $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEFAULT_APP_TARGET_SDK := $(call module-target-sdk-version) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SDK_VERSION := $(call module-sdk-version) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MIN_SDK_VERSION := $(call codename-or-sdk-to-sdk,$(call module-min-sdk-version)) ================================================ FILE: core/java_common.mk ================================================ # Common to host and target Java modules. my_soong_problems := ifneq ($(filter ../%,$(LOCAL_SRC_FILES)),) my_soong_problems += dotdot_srcs endif ########################################################### ## Java version ########################################################### # Use the LOCAL_JAVA_LANGUAGE_VERSION if it is set, otherwise # use one based on the LOCAL_SDK_VERSION. # # The LOCAL_SDK_VERSION behavior is to ensure that, by default, # code that is expected to run on older releases of Android # does not use any 1.8 language features that are not supported # on earlier runtimes (like default / static interface methods). # Modules can override this logic by specifying # LOCAL_JAVA_LANGUAGE_VERSION explicitly. ifeq (,$(LOCAL_JAVA_LANGUAGE_VERSION)) ifdef LOCAL_IS_HOST_MODULE # Host modules always default to 1.9 LOCAL_JAVA_LANGUAGE_VERSION := 1.9 else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_1_9_SUPPORT))) LOCAL_JAVA_LANGUAGE_VERSION := 1.8 else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_11_SUPPORT))) LOCAL_JAVA_LANGUAGE_VERSION := 1.9 else ifneq (,$(filter $(LOCAL_SDK_VERSION), $(TARGET_SDK_VERSIONS_WITHOUT_JAVA_17_SUPPORT))) LOCAL_JAVA_LANGUAGE_VERSION := 11 else ifneq (,$(LOCAL_SDK_VERSION)$(TARGET_BUILD_USE_PREBUILT_SDKS)) # TODO(ccross): allow 1.9 for current and unbundled once we have SDK system modules LOCAL_JAVA_LANGUAGE_VERSION := 1.8 else ifeq ($(RELEASE_TARGET_JAVA_21),true) LOCAL_JAVA_LANGUAGE_VERSION := 21 else LOCAL_JAVA_LANGUAGE_VERSION := 17 endif endif endif LOCAL_JAVACFLAGS += -source $(LOCAL_JAVA_LANGUAGE_VERSION) -target $(LOCAL_JAVA_LANGUAGE_VERSION) ########################################################### # OpenJDK versions up to 8 shipped with bootstrap and tools jars # (rt.jar, jce.jar, tools.jar etc.). These are no longer part of # OpenJDK 9, but we still make them available for host tools that # are targeting older versions. USE_HOST_BOOTSTRAP_JARS := true ifeq (,$(filter $(LOCAL_JAVA_LANGUAGE_VERSION), 1.6 1.7 1.8)) USE_HOST_BOOTSTRAP_JARS := false endif ########################################################### # Drop HOST_JDK_TOOLS_JAR from classpath when targeting versions > 9 (which don't have it). # TODO: Remove HOST_JDK_TOOLS_JAR and all references to it once host # bootstrap jars are no longer supported (ie. when USE_HOST_BOOTSTRAP_JARS # is always false). http://b/38418220 ifneq ($(USE_HOST_BOOTSTRAP_JARS),true) LOCAL_CLASSPATH := $(filter-out $(HOST_JDK_TOOLS_JAR),$(LOCAL_CLASSPATH)) endif ########################################################### ## .proto files: Compile proto files to .java ########################################################### ifeq ($(strip $(LOCAL_PROTOC_OPTIMIZE_TYPE)),) LOCAL_PROTOC_OPTIMIZE_TYPE := lite endif proto_sources := $(filter %.proto,$(LOCAL_SRC_FILES)) ifneq ($(proto_sources),) proto_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(proto_sources)) proto_java_intemediate_dir := $(intermediates.COMMON)/proto proto_java_sources_dir := $(proto_java_intemediate_dir)/src proto_java_srcjar := $(intermediates.COMMON)/proto.srcjar LOCAL_SRCJARS += $(proto_java_srcjar) $(proto_java_srcjar): PRIVATE_PROTO_INCLUDES := $(TOP) $(proto_java_srcjar): PRIVATE_PROTO_SRC_FILES := $(proto_sources_fullpath) $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_DIR := $(proto_java_sources_dir) $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS := $(LOCAL_PROTOC_FLAGS) ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),micro) $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javamicro_out $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javamicro else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),nano) $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javanano_out $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javanano $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javanano else ifeq ($(LOCAL_PROTOC_OPTIMIZE_TYPE),stream) $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --javastream_out $(proto_java_srcjar): PRIVATE_PROTOC_FLAGS += --plugin=$(HOST_OUT_EXECUTABLES)/protoc-gen-javastream $(proto_java_srcjar): $(HOST_OUT_EXECUTABLES)/protoc-gen-javastream else $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_OPTION := --java_out endif $(proto_java_srcjar): PRIVATE_PROTO_JAVA_OUTPUT_PARAMS := $(if $(filter lite,$(LOCAL_PROTOC_OPTIMIZE_TYPE)),lite$(if $(LOCAL_PROTO_JAVA_OUTPUT_PARAMS),:,),)$(LOCAL_PROTO_JAVA_OUTPUT_PARAMS) $(proto_java_srcjar) : $(proto_sources_fullpath) $(PROTOC) $(SOONG_ZIP) $(call transform-proto-to-java) #TODO: protoc should output the dependencies introduced by imports. ALL_MODULES.$(my_register_name).PROTO_FILES := $(proto_sources_fullpath) endif # proto_sources ######################################### ## Java resources # Look for resource files in any specified directories. # Non-java and non-doc files will be picked up as resources # and included in the output jar file. java_resource_file_groups := LOCAL_JAVA_RESOURCE_DIRS := $(strip $(LOCAL_JAVA_RESOURCE_DIRS)) ifneq ($(LOCAL_JAVA_RESOURCE_DIRS),) # This makes a list of words like # ::: :: : # where each of the files is relative to the directory it's grouped with. # Directories that don't contain any resource files will result in groups # that end with a colon, and they are stripped out in the next step. java_resource_file_groups += \ $(foreach dir,$(LOCAL_JAVA_RESOURCE_DIRS), \ $(subst $(space),:,$(strip \ $(LOCAL_PATH)/$(dir): \ $(patsubst ./%,%,$(sort $(shell cd $(LOCAL_PATH)/$(dir) && \ find . \ -type d -a -name ".svn" -prune -o \ -type f \ -a \! -name "*.java" \ -a \! -name "package.html" \ -a \! -name "overview.html" \ -a \! -name ".*.swp" \ -a \! -name ".DS_Store" \ -a \! -name "*~" \ -print \ ))) \ )) \ ) java_resource_file_groups := $(filter-out %:,$(java_resource_file_groups)) endif # LOCAL_JAVA_RESOURCE_DIRS ifneq ($(LOCAL_JAVA_RESOURCE_FILES),) # Converts LOCAL_JAVA_RESOURCE_FILES := to $(dir $(file))::$(notdir $(file)) # and LOCAL_JAVA_RESOURCE_FILES :=

: to :: java_resource_file_groups += $(strip $(foreach res,$(LOCAL_JAVA_RESOURCE_FILES), \ $(eval _file := $(call word-colon,2,$(res))) \ $(if $(_file), \ $(eval _base := $(call word-colon,1,$(res))), \ $(eval _base := $(dir $(res))) \ $(eval _file := $(notdir $(res)))) \ $(if $(filter /%, \ $(filter-out $(OUT_DIR)/%,$(_base) $(_file))), \ $(call pretty-error,LOCAL_JAVA_RESOURCE_FILES may not include absolute paths: $(_base) $(_file))) \ $(patsubst %/,%,$(_base))::$(_file))) endif # LOCAL_JAVA_RESOURCE_FILES ifdef java_resource_file_groups # The full paths to all resources, used for dependencies. java_resource_sources := \ $(foreach group,$(java_resource_file_groups), \ $(addprefix $(word 1,$(subst :,$(space),$(group)))/, \ $(wordlist 2,9999,$(subst :,$(space),$(group))) \ ) \ ) # The arguments to jar that will include these files in a jar file. # Quote the file name to handle special characters (such as #) correctly. extra_jar_args := \ $(foreach group,$(java_resource_file_groups), \ $(addprefix -C "$(word 1,$(subst :,$(space),$(group)))" , \ $(foreach w, $(wordlist 2,9999,$(subst :,$(space),$(group))), "$(w)" ) \ ) \ ) java_resource_file_groups := else java_resource_sources := extra_jar_args := endif # java_resource_file_groups ##################################### ## Warn if there is unrecognized file in LOCAL_SRC_FILES. my_unknown_src_files := $(filter-out \ %.java %.aidl %.proto %.logtags, \ $(LOCAL_SRC_FILES) $(LOCAL_INTERMEDIATE_SOURCES) $(LOCAL_GENERATED_SOURCES)) ifneq ($(my_unknown_src_files),) $(warning $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): Unused source files: $(my_unknown_src_files)) endif ###################################### ## PRIVATE java vars # LOCAL_SOURCE_FILES_ALL_GENERATED is set only if the module does not have static source files, # but generated source files. # You have to set up the dependency in some other way. need_compile_java := $(strip $(all_java_sources)$(LOCAL_SRCJARS)$(all_res_assets)$(java_resource_sources))$(LOCAL_STATIC_JAVA_LIBRARIES)$(filter true,$(LOCAL_SOURCE_FILES_ALL_GENERATED)) ifdef need_compile_java annotation_processor_flags := annotation_processor_deps := annotation_processor_jars := # If error prone is enabled then add LOCAL_ERROR_PRONE_FLAGS to LOCAL_JAVACFLAGS ifeq ($(RUN_ERROR_PRONE),true) annotation_processor_jars += $(ERROR_PRONE_JARS) LOCAL_JAVACFLAGS += $(ERROR_PRONE_FLAGS) LOCAL_JAVACFLAGS += '-Xplugin:ErrorProne $(ERROR_PRONE_CHECKS) $(LOCAL_ERROR_PRONE_FLAGS)' endif ifdef LOCAL_ANNOTATION_PROCESSORS annotation_processor_jars += $(call java-lib-files,$(LOCAL_ANNOTATION_PROCESSORS),true) # b/25860419: annotation processors must be explicitly specified for grok annotation_processor_flags += $(foreach class,$(LOCAL_ANNOTATION_PROCESSOR_CLASSES),-processor $(class)) endif ifneq (,$(strip $(annotation_processor_jars))) annotation_processor_flags += -processorpath $(call normalize-path-list,$(annotation_processor_jars)) annotation_processor_deps += $(annotation_processor_jars) endif full_static_java_libs := $(call java-lib-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE)) full_static_java_header_libs := $(call java-lib-header-files,$(LOCAL_STATIC_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE)) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_LIBRARIES := $(full_static_java_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_HEADER_LIBRARIES := $(full_static_java_header_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates.COMMON)/classes $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANNO_INTERMEDIATES_DIR := $(intermediates.COMMON)/anno $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/src $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HAS_RS_SOURCES := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCE_LIST := $(java_source_list_file) # Quickly check class path vars. disallowed_deps := $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),$(call resolve-prebuilt-sdk-module,$(sdk))) disallowed_deps += $(foreach sdk,$(TARGET_AVAILABLE_SDK_VERSIONS),\ $(foreach sdk_lib,$(JAVA_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(sdk),$(sdk_lib)))) bad_deps := $(filter $(disallowed_deps),$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES)) ifneq (,$(bad_deps)) $(call pretty-error,SDK modules should not be depended on directly. Please use LOCAL_SDK_VERSION for $(bad_deps)) endif full_java_bootclasspath_libs := empty_bootclasspath := my_system_modules := exported_sdk_libs_files := my_exported_sdk_libs_file := ifndef LOCAL_IS_HOST_MODULE sdk_libs := # When an sdk lib name is listed in LOCAL_JAVA_LIBRARIES, move it to LOCAL_SDK_LIBRARIES, so that # it is correctly redirected to the stubs library. LOCAL_SDK_LIBRARIES += $(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) LOCAL_JAVA_LIBRARIES := $(filter-out $(JAVA_SDK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) ifeq ($(LOCAL_SDK_VERSION),) ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) # No bootclasspath. But we still need "" to prevent javac from using default host bootclasspath. empty_bootclasspath := "" # Most users of LOCAL_NO_STANDARD_LIBRARIES really mean no framework libs, # and manually add back the core libs. The ones that don't are in soong # now, so just always assume that they want the default system modules my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) else # LOCAL_NO_STANDARD_LIBRARIES full_java_bootclasspath_libs := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES)) LOCAL_JAVA_LIBRARIES := $(filter-out $(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES)) my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) endif # LOCAL_NO_STANDARD_LIBRARIES ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)) sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,system_current,$(lib_name))) else # When SDK libraries are referenced from modules built without SDK, provide the all APIs to them sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name)) endif else ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) $(call pretty-error,Must not define both LOCAL_NO_STANDARD_LIBRARIES and LOCAL_SDK_VERSION) endif ifeq ($(strip $(filter $(LOCAL_SDK_VERSION),$(TARGET_AVAILABLE_SDK_VERSIONS))),) $(call pretty-error,Invalid LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)' \ Choices are: $(TARGET_AVAILABLE_SDK_VERSIONS)) endif ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(filter-out %current,$(LOCAL_SDK_VERSION))) # TARGET_BUILD_USE_PREBUILT_SDKS mode or numbered SDK. Use prebuilt modules. sdk_module := $(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION)) sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,$(LOCAL_SDK_VERSION),$(lib_name))) else # Note: the lib naming scheme must be kept in sync with build/soong/java/sdk_library.go. sdk_lib_suffix = $(call pretty-error,sdk_lib_suffix was not set correctly) ifeq (current,$(LOCAL_SDK_VERSION)) sdk_module := $(ANDROID_PUBLIC_STUBS) sdk_lib_suffix := .stubs else ifeq (system_current,$(LOCAL_SDK_VERSION)) sdk_module := $(ANDROID_SYSTEM_STUBS) sdk_lib_suffix := .stubs.system else ifeq (test_current,$(LOCAL_SDK_VERSION)) sdk_module := $(ANDROID_TEST_STUBS) sdk_lib_suffix := .stubs.test else ifeq (core_current,$(LOCAL_SDK_VERSION)) sdk_module := $(ANDROID_CORE_STUBS) sdk_lib_suffix = $(call pretty-error,LOCAL_SDK_LIBRARIES not supported for LOCAL_SDK_VERSION = core_current) endif sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name)$(sdk_lib_suffix)) endif full_java_bootclasspath_libs := $(call java-lib-header-files,$(sdk_module)) endif # LOCAL_SDK_VERSION ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) ifneq ($(LOCAL_MODULE),jacocoagent) ifeq ($(EMMA_INSTRUMENT),true) ifneq ($(EMMA_INSTRUMENT_STATIC),true) # For instrumented build, if Jacoco is not being included statically # in instrumented packages then include Jacoco classes into the # bootclasspath. full_java_bootclasspath_libs += $(call java-lib-header-files,jacocoagent) endif # EMMA_INSTRUMENT_STATIC endif # EMMA_INSTRUMENT endif # LOCAL_MODULE == jacocoagent endif # LOCAL_NO_STANDARD_LIBRARIES # In order to compile lambda code javac requires various invokedynamic- # related classes to be present. This change adds stubs needed for # javac to compile lambdas. ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) ifdef TARGET_BUILD_USE_PREBUILT_SDKS full_java_bootclasspath_libs += $(call java-lib-header-files,sdk-core-lambda-stubs) else full_java_bootclasspath_libs += $(call java-lib-header-files,core-lambda-stubs) endif endif full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE)) full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES) $(sdk_libs),$(LOCAL_IS_HOST_MODULE)) sdk_libs := # Files that contains the names of SDK libraries exported from dependencies. These will be re-exported. # Note: No need to consider LOCAL_*_ANDROID_LIBRARIES and LOCAL_STATIC_JAVA_AAR_LIBRARIES. They are all appended to # LOCAL_*_JAVA_LIBRARIES in java.mk exported_sdk_libs_files := $(call exported-sdk-libs-files,$(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES)) # The file that contains the names of all SDK libraries that this module exports and re-exports my_exported_sdk_libs_file := $(call local-intermediates-dir,COMMON)/exported-sdk-libs else # LOCAL_IS_HOST_MODULE ifeq ($(USE_CORE_LIB_BOOTCLASSPATH),true) ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true) empty_bootclasspath := "" else full_java_bootclasspath_libs := $(call java-lib-header-files,$(addsuffix -hostdex,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES)),true) endif my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES) full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),true) full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES),true) else # !USE_CORE_LIB_BOOTCLASSPATH # Give host-side tools a version of OpenJDK's standard libraries # close to what they're targeting. As of Dec 2017, AOSP is only # bundling OpenJDK 8 and 9, so nothing < 8 is available. # # When building with OpenJDK 8, the following should have no # effect since those jars would be available by default. # # When building with OpenJDK 9 but targeting a version < 1.8, # putting them on the bootclasspath means that: # a) code can't (accidentally) refer to OpenJDK 9 specific APIs # b) references to existing APIs are not reinterpreted in an # OpenJDK 9-specific way, eg. calls to subclasses of # java.nio.Buffer as in http://b/70862583 ifeq ($(USE_HOST_BOOTSTRAP_JARS),true) full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/jce.jar full_java_bootclasspath_libs += $(ANDROID_JAVA8_HOME)/jre/lib/rt.jar endif full_shared_java_libs := $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/,\ $(addsuffix $(COMMON_JAVA_PACKAGE_SUFFIX),$(LOCAL_JAVA_LIBRARIES))) full_shared_java_header_libs := $(full_shared_java_libs) endif # USE_CORE_LIB_BOOTCLASSPATH endif # !LOCAL_IS_HOST_MODULE # (b/204397180) Record ALL_DEPS by default. ALL_MODULES.$(my_register_name).ALL_DEPS := $(ALL_MODULES.$(my_register_name).ALL_DEPS) $(full_java_bootclasspath_libs) # Export the SDK libs. The sdk library names listed in LOCAL_SDK_LIBRARIES are first exported. # Then sdk library names exported from dependencies are all re-exported. $(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS_FILES := $(exported_sdk_libs_files) $(my_exported_sdk_libs_file): PRIVATE_SDK_LIBS := $(sort $(LOCAL_SDK_LIBRARIES)) $(my_exported_sdk_libs_file): $(exported_sdk_libs_files) @echo "Export SDK libs $@" $(hide) mkdir -p $(dir $@) && rm -f $@ $@.temp $(if $(PRIVATE_SDK_LIBS),\ echo $(PRIVATE_SDK_LIBS) | tr ' ' '\n' > $@.temp,\ touch $@.temp) $(if $(PRIVATE_EXPORTED_SDK_LIBS_FILES),\ cat $(PRIVATE_EXPORTED_SDK_LIBS_FILES) >> $@.temp) $(hide) cat $@.temp | sort -u > $@ $(hide) rm -f $@.temp ifdef empty_bootclasspath ifdef full_java_bootclasspath_libs $(call pretty-error,internal error: empty_bootclasspath and full_java_bootclasspath_libs should not both be set) endif endif full_java_system_modules_deps := my_system_modules_dir := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES := ifeq (,$(filter $(LOCAL_JAVA_LANGUAGE_VERSION),$(JAVA_LANGUAGE_VERSIONS_WITHOUT_SYSTEM_MODULES))) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_USE_SYSTEM_MODULES := true ifdef my_system_modules ifneq ($(my_system_modules),none) ifndef SOONG_SYSTEM_MODULES_$(my_system_modules) $(call pretty-error, Invalid system modules $(my_system_modules)) endif full_java_system_modules_deps := $(SOONG_SYSTEM_MODULES_DEPS_$(my_system_modules)) my_system_modules_dir := $(SOONG_SYSTEM_MODULES_$(my_system_modules)) endif endif endif $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := $(full_java_bootclasspath_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EMPTY_BOOTCLASSPATH := $(empty_bootclasspath) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES := $(my_system_modules) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_DIR := $(my_system_modules_dir) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SYSTEM_MODULES_LIBS := $(call java-lib-files,$(SOONG_SYSTEM_MODULES_LIBS_$(my_system_modules))) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PATCH_MODULE := $(LOCAL_PATCH_MODULE) ifndef LOCAL_IS_HOST_MODULE # This is set by packages that are linking to other packages that export # shared libraries, allowing them to make use of the code in the linked apk. apk_libraries := $(sort $(LOCAL_APK_LIBRARIES) $(LOCAL_RES_LIBRARIES)) ifneq ($(apk_libraries),) link_apk_libraries := $(call app-lib-files,$(apk_libraries)) link_apk_header_libs := $(call app-lib-header-files,$(apk_libraries)) # link against the jar with full original names (before proguard processing). full_shared_java_libs += $(link_apk_libraries) full_shared_java_header_libs += $(link_apk_header_libs) endif # This is set by packages that contain instrumentation, allowing them to # link against the package they are instrumenting. Currently only one such # package is allowed. LOCAL_INSTRUMENTATION_FOR := $(strip $(LOCAL_INSTRUMENTATION_FOR)) ifdef LOCAL_INSTRUMENTATION_FOR ifneq ($(words $(LOCAL_INSTRUMENTATION_FOR)),1) $(error \ $(LOCAL_PATH): Multiple LOCAL_INSTRUMENTATION_FOR members defined) endif link_instr_intermediates_dir.COMMON := $(call intermediates-dir-for, \ APPS,$(LOCAL_INSTRUMENTATION_FOR),,COMMON) # link against the jar with full original names (before proguard processing). link_instr_classes_jar := $(link_instr_intermediates_dir.COMMON)/classes-pre-proguard.jar ifneq ($(TURBINE_ENABLED),false) link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes-header.jar else link_instr_classes_header_jar := $(link_instr_intermediates_dir.COMMON)/classes.jar endif full_shared_java_libs += $(link_instr_classes_jar) full_shared_java_header_libs += $(link_instr_classes_header_jar) endif # LOCAL_INSTRUMENTATION_FOR endif # LOCAL_IS_HOST_MODULE endif # need_compile_java # We may want to add jar manifest or jar resource files even if there is no java code at all. $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) jar_manifest_file := ifneq ($(strip $(LOCAL_JAR_MANIFEST)),) jar_manifest_file := $(LOCAL_PATH)/$(LOCAL_JAR_MANIFEST) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := $(jar_manifest_file) else $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := endif ########################################################## full_java_libs := $(full_shared_java_libs) $(full_static_java_libs) $(LOCAL_CLASSPATH) full_java_header_libs := $(full_shared_java_header_libs) $(full_static_java_header_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_LIBRARIES := $(full_java_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_JAVA_HEADER_LIBRARIES := $(full_java_header_libs) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SHARED_JAVA_HEADER_LIBRARIES := $(full_shared_java_header_libs) ########################################################### # Verify that all libraries are safe to use ########################################################### ifndef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_SDK_VERSION),system_current) my_link_type := java:system my_warn_types := my_allowed_types := java:sdk java:system java:core else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) my_link_type := java:system my_warn_types := my_allowed_types := java:sdk java:system java:core else ifeq ($(LOCAL_SDK_VERSION),core_current) my_link_type := java:core my_warn_types := my_allowed_types := java:core else ifneq ($(LOCAL_SDK_VERSION),) my_link_type := java:sdk my_warn_types := my_allowed_types := java:sdk java:core else my_link_type := java:platform my_warn_types := my_allowed_types := java:sdk java:system java:platform java:core endif my_link_deps := $(addprefix JAVA_LIBRARIES:,$(LOCAL_STATIC_JAVA_LIBRARIES) $(LOCAL_JAVA_LIBRARIES)) my_link_deps += $(addprefix APPS:,$(apk_libraries)) my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := COMMON include $(BUILD_SYSTEM)/link_type.mk endif # !LOCAL_IS_HOST_MODULE ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) SOONG_CONV.$(LOCAL_MODULE).PROBLEMS := \ $(SOONG_CONV.$(LOCAL_MODULE).PROBLEMS) $(my_soong_problems) SOONG_CONV.$(LOCAL_MODULE).DEPS := \ $(SOONG_CONV.$(LOCAL_MODULE).DEPS) \ $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(LOCAL_JAVA_LIBRARIES) \ $(LOCAL_JNI_SHARED_LIBRARIES) SOONG_CONV.$(LOCAL_MODULE).TYPE := java SOONG_CONV.$(LOCAL_MODULE).MAKEFILES := \ $(SOONG_CONV.$(LOCAL_MODULE).MAKEFILES) $(LOCAL_MODULE_MAKEFILE) SOONG_CONV.$(LOCAL_MODULE).INSTALLED := \ $(SOONG_CONV.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE) SOONG_CONV := $(SOONG_CONV) $(LOCAL_MODULE) endif ================================================ FILE: core/java_host_test_config_template.xml ================================================ ================================================ FILE: core/java_host_unit_test_config_template.xml ================================================ ================================================ FILE: core/java_library.mk ================================================ ########################################################### ## Standard rules for building a java library. ## ########################################################### $(call record-module-type,JAVA_LIBRARY) ifdef LOCAL_IS_HOST_MODULE $(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY) endif LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX) LOCAL_MODULE_CLASS := JAVA_LIBRARIES ifneq (,$(LOCAL_ASSET_DIR)) $(error $(LOCAL_PATH): Target java libraries may not set LOCAL_ASSET_DIR) endif ifneq (true,$(LOCAL_IS_STATIC_JAVA_LIBRARY)) ifneq (,$(LOCAL_RESOURCE_DIR)) $(error $(LOCAL_PATH): Target java libraries may not set LOCAL_RESOURCE_DIR) endif # base_rules.mk looks at this all_res_assets := endif LOCAL_BUILT_MODULE_STEM := javalib.jar # For java libraries, other modules should depend on # out/target/common/obj/JAVA_LIBRARIES/.../classes.jar. # There are some dependencies outside the build system that assume static # java libraries produce javalib.jar, so we will copy classes.jar there too. intermediates.COMMON := $(call local-intermediates-dir,COMMON) common_javalib.jar := $(intermediates.COMMON)/javalib.jar dex_preopt_profile_src_file := $(common_javalib.jar) LOCAL_INTERMEDIATE_TARGETS += $(common_javalib.jar) ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) LOCAL_PROGUARD_ENABLED := endif ifeq (true,$(EMMA_INSTRUMENT)) ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) ifeq (true,$(EMMA_INSTRUMENT_STATIC)) LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent # Exclude jacoco classes from proguard LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags endif # LOCAL_EMMA_INSTRUMENT endif # EMMA_INSTRUMENT_STATIC else LOCAL_EMMA_INSTRUMENT := false endif # EMMA_INSTRUMENT my_dex_jar := $(common_javalib.jar) ################################# include $(BUILD_SYSTEM)/java.mk ################################# ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true) # There are some dependencies outside the build system that assume classes.jar # is available as javalib.jar so copy it there too. $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(common_javalib.jar))) $(eval $(call copy-one-file,$(full_classes_pre_proguard_jar),$(LOCAL_BUILT_MODULE))) else # !LOCAL_IS_STATIC_JAVA_LIBRARY $(common_javalib.jar): PRIVATE_DEX_FILE := $(built_dex) $(common_javalib.jar): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) $(common_javalib.jar): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP) $(common_javalib.jar) : $(full_classes_pre_proguard_jar) $(built_dex) $(java_resource_sources) | $(ZIPTIME) $(ZIPALIGN) @echo "target Jar: $(PRIVATE_MODULE) ($@)" rm -rf $@.parts && mkdir -p $@.parts $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) $(MERGE_ZIPS) -j $@.tmp $@.parts/dex.zip $@.parts/res.zip rm -rf $@.parts $(hide) $(ZIPTIME) $@.tmp $(call commit-change-for-toc,$@) ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) $(uncompress-dexs) $(align-package) endif # LOCAL_UNCOMPRESS_DEX .KATI_RESTAT: $(common_javalib.jar) $(eval $(call copy-one-file,$(common_javalib.jar),$(LOCAL_BUILT_MODULE))) endif # !LOCAL_IS_STATIC_JAVA_LIBRARY $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=JAVA_LIBRARY)) ================================================ FILE: core/java_prebuilt_internal.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################ # Internal build rules for JAVA_LIBRARIES prebuilt modules ############################################################ ifneq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS)) $(call pretty-error,java_prebuilt_internal.mk is for JAVA_LIBRARIES modules only) endif include $(BUILD_SYSTEM)/base_rules.mk built_module := $(LOCAL_BUILT_MODULE) ifeq (,$(LOCAL_IS_HOST_MODULE)$(filter true,$(LOCAL_UNINSTALLABLE_MODULE))) prebuilt_module_is_dex_javalib := true else prebuilt_module_is_dex_javalib := endif ifeq ($(prebuilt_module_is_dex_javalib),true) my_dex_jar := $(my_prebuilt_src_file) # This is a target shared library, i.e. a jar with classes.dex. $(foreach pair,$(PRODUCT_BOOT_JARS), \ $(if $(filter $(LOCAL_MODULE),$(call word-colon,2,$(pair))), \ $(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files))) ALL_MODULES.$(my_register_name).CLASSES_JAR := $(common_classes_jar) ####################################### # defines built_odex along with rule to install odex my_manifest_or_apk := $(my_prebuilt_src_file) include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk my_manifest_or_apk := ####################################### $(built_module) : $(my_prebuilt_src_file) $(call copy-file-to-target) else # ! prebuilt_module_is_dex_javalib $(built_module) : $(my_prebuilt_src_file) $(transform-prebuilt-to-target) endif # ! prebuilt_module_is_dex_javalib my_src_jar := $(my_prebuilt_src_file) ifdef LOCAL_IS_HOST_MODULE # for host java libraries deps should be in the common dir, so we make a copy in # the common dir. common_classes_jar := $(intermediates.COMMON)/classes.jar common_header_jar := $(intermediates.COMMON)/classes-header.jar $(common_classes_jar): PRIVATE_MODULE := $(LOCAL_MODULE) $(common_classes_jar): PRIVATE_PREFIX := $(my_prefix) $(common_classes_jar) : $(my_src_jar) $(transform-prebuilt-to-target) ifneq ($(TURBINE_ENABLED),false) $(common_header_jar) : $(my_src_jar) $(transform-prebuilt-to-target) endif else # !LOCAL_IS_HOST_MODULE # for target java libraries, the LOCAL_BUILT_MODULE is in a product-specific dir, # while the deps should be in the common dir, so we make a copy in the common dir. common_classes_jar := $(intermediates.COMMON)/classes.jar common_header_jar := $(intermediates.COMMON)/classes-header.jar common_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar common_javalib_jar := $(intermediates.COMMON)/javalib.jar $(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_MODULE := $(LOCAL_MODULE) $(common_classes_jar) $(common_classes_pre_proguard_jar) $(common_javalib_jar): PRIVATE_PREFIX := $(my_prefix) ifeq ($(LOCAL_SDK_VERSION),system_current) my_link_type := java:system else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) my_link_type := java:system else ifeq ($(LOCAL_SDK_VERSION),core_current) my_link_type := java:core else ifneq ($(LOCAL_SDK_VERSION),) my_link_type := java:sdk else my_link_type := java:platform endif # TODO: check dependencies of prebuilt files my_link_deps := my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := COMMON include $(BUILD_SYSTEM)/link_type.mk ifeq ($(prebuilt_module_is_dex_javalib),true) # For prebuilt shared Java library we don't have classes.jar. $(common_javalib_jar) : $(my_src_jar) $(transform-prebuilt-to-target) else # ! prebuilt_module_is_dex_javalib my_src_aar := $(filter %.aar, $(my_prebuilt_src_file)) ifneq ($(my_src_aar),) # This is .aar file, archive of classes.jar and Android resources. my_src_jar := $(intermediates.COMMON)/aar/classes.jar my_src_proguard_options := $(intermediates.COMMON)/aar/proguard.txt my_src_android_manifest := $(intermediates.COMMON)/aar/AndroidManifest.xml $(my_src_jar) : .KATI_IMPLICIT_OUTPUTS := $(my_src_proguard_options) $(my_src_jar) : .KATI_IMPLICIT_OUTPUTS += $(my_src_android_manifest) $(my_src_jar) : $(my_src_aar) $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) $(dir $@)/res $(hide) unzip -qoDD -d $(dir $@) $< # Make sure the proguard and AndroidManifest.xml files exist $(hide) touch $(dir $@)/proguard.txt $(hide) touch $(dir $@)/AndroidManifest.xml my_prebuilt_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml $(eval $(call copy-one-file,$(my_src_android_manifest),$(my_prebuilt_android_manifest))) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_prebuilt_android_manifest)) endif $(common_classes_jar) : $(my_src_jar) $(transform-prebuilt-to-target) ifneq ($(TURBINE_ENABLED),false) $(common_header_jar) : $(my_src_jar) $(transform-prebuilt-to-target) endif $(common_classes_pre_proguard_jar) : $(my_src_jar) $(transform-prebuilt-to-target) $(common_javalib_jar) : $(common_classes_jar) $(transform-prebuilt-to-target) include $(BUILD_SYSTEM)/force_aapt2.mk ifneq ($(my_src_aar),) $(intermediates.COMMON)/export_proguard_flags : $(my_src_proguard_options) $(transform-prebuilt-to-target) LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) ifeq ($(LOCAL_SDK_RES_VERSION),) LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) endif framework_res_package_export := # Please refer to package.mk ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) framework_res_package_export := \ $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) else framework_res_package_export := \ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk endif endif # transitive-res-packages is only populated for Soong modules for now, but needs # to exist so that other Make modules can depend on it. Create an empty file. my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages $(my_transitive_res_packages): touch $@ my_res_package := $(intermediates.COMMON)/package-res.apk # We needed only very few PRIVATE variables and aapt2.mk input variables. Reset the unnecessary ones. $(my_res_package): PRIVATE_AAPT2_CFLAGS := $(my_res_package): PRIVATE_AAPT_FLAGS := --static-lib --no-static-lib-packages --auto-add-overlay $(my_res_package): PRIVATE_ANDROID_MANIFEST := $(my_src_android_manifest) $(my_res_package): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export) $(my_res_package): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(my_res_package): PRIVATE_PROGUARD_OPTIONS_FILE := $(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK := $(my_res_package): PRIVATE_DEFAULT_APP_TARGET_SDK := $(my_res_package): PRIVATE_PRODUCT_AAPT_CONFIG := $(my_res_package): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := $(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(my_res_package) : $(framework_res_package_export) $(my_res_package) : $(my_src_android_manifest) full_android_manifest := my_res_resources := my_overlay_resources := my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res R_file_stamp := proguard_options_file := my_generated_res_dirs := $(intermediates.COMMON)/aar/res my_generated_res_dirs_deps := $(my_src_jar) include $(BUILD_SYSTEM)/aapt2.mk # Make sure my_res_package is created when you run mm/mmm. $(built_module) : $(my_res_package) endif # $(my_src_aar) # make sure the classes.jar and javalib.jar are built before $(LOCAL_BUILT_MODULE) $(built_module) : $(common_javalib_jar) my_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs $(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES) $(my_exported_sdk_libs_file): @echo "Export SDK libs $@" $(hide) mkdir -p $(dir $@) && rm -f $@ $(if $(PRIVATE_EXPORTED_SDK_LIBS),\ $(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\n' > $@,\ $(hide) touch $@) endif # ! prebuilt_module_is_dex_javalib endif # LOCAL_IS_HOST_MODULE is not set ================================================ FILE: core/java_renderscript.mk ================================================ ############################################################### ## Renderscript support for java ## Adds rules to convert .rscript files to .java and .bc files ############################################################### renderscript_sources := $(filter %.rscript,$(LOCAL_SRC_FILES)) LOCAL_SRC_FILES := $(filter-out %.rscript,$(LOCAL_SRC_FILES)) rs_generated_res_zip := rs_generated_src_jar := rs_compatibility_jni_libs := ifneq ($(renderscript_sources),) renderscript_sources_fullpath := $(addprefix $(LOCAL_PATH)/, $(renderscript_sources)) renderscript_intermediate.COMMON := $(intermediates.COMMON)/renderscript rs_generated_res_zip := $(renderscript_intermediate.COMMON)/res.zip rs_generated_src_jar := $(renderscript_intermediate.COMMON)/rs.srcjar LOCAL_SRCJARS += $(rs_generated_src_jar) # Defaulting to an empty string uses the latest available platform SDK. renderscript_target_api := ifneq (,$(LOCAL_RENDERSCRIPT_TARGET_API)) renderscript_target_api := $(LOCAL_RENDERSCRIPT_TARGET_API) else ifneq (,$(LOCAL_SDK_VERSION)) # Set target-api for LOCAL_SDK_VERSIONs other than current. ifneq (,$(filter-out current system_current test_current core_current, $(LOCAL_SDK_VERSION))) renderscript_target_api := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) endif endif # LOCAL_SDK_VERSION is set endif # LOCAL_RENDERSCRIPT_TARGET_API is set # For 64-bit, we always have to upgrade to at least 21 for compat build. ifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),) ifeq ($(TARGET_IS_64_BIT),true) ifneq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),) renderscript_target_api := 21 endif endif endif ifeq ($(LOCAL_RENDERSCRIPT_CC),) LOCAL_RENDERSCRIPT_CC := $(LLVM_RS_CC) endif # Turn on all warnings and warnings as errors for RS compiles. # This can be disabled with LOCAL_RENDERSCRIPT_FLAGS := -Wno-error renderscript_flags := -Wall -Werror renderscript_flags += $(LOCAL_RENDERSCRIPT_FLAGS) # prepend the RenderScript system include path ifneq ($(filter-out current system_current test_current core_current,$(LOCAL_SDK_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_VERSION))),) # if a numeric LOCAL_SDK_VERSION, or current LOCAL_SDK_VERSION with TARGET_BUILD_USE_PREBUILT_SDKS LOCAL_RENDERSCRIPT_INCLUDES := \ $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/clang-include \ $(HISTORICAL_SDK_VERSIONS_ROOT)/renderscript/include \ $(LOCAL_RENDERSCRIPT_INCLUDES) else LOCAL_RENDERSCRIPT_INCLUDES := \ $(TOPDIR)external/clang/lib/Headers \ $(TOPDIR)frameworks/rs/script_api/include \ $(LOCAL_RENDERSCRIPT_INCLUDES) endif ifneq ($(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE),) LOCAL_RENDERSCRIPT_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE) endif bc_files := $(patsubst %.rscript,%.bc, $(notdir $(renderscript_sources))) bc_dep_files := $(addprefix $(renderscript_intermediate.COMMON)/,$(patsubst %.bc,%.d,$(bc_files))) $(rs_generated_src_jar): PRIVATE_RS_INCLUDES := $(LOCAL_RENDERSCRIPT_INCLUDES) $(rs_generated_src_jar): PRIVATE_RS_CC := $(LOCAL_RENDERSCRIPT_CC) $(rs_generated_src_jar): PRIVATE_RS_FLAGS := $(renderscript_flags) $(rs_generated_src_jar): PRIVATE_RS_SOURCE_FILES := $(renderscript_sources_fullpath) $(rs_generated_src_jar): PRIVATE_RS_OUTPUT_DIR := $(renderscript_intermediate.COMMON) $(rs_generated_src_jar): PRIVATE_RS_TARGET_API := $(patsubst current,0,$(renderscript_target_api)) $(rs_generated_src_jar): PRIVATE_DEP_FILES := $(bc_dep_files) $(rs_generated_src_jar): PRIVATE_RS_OUTPUT_RES_ZIP := $(rs_generated_res_zip) $(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS := $(rs_generated_res_zip) $(rs_generated_src_jar): $(renderscript_sources_fullpath) $(LOCAL_RENDERSCRIPT_CC) $(SOONG_ZIP) $(transform-renderscripts-to-java-and-bc) # include the dependency files (.d) generated by llvm-rs-cc. $(call include-depfile,$(rs_generated_src_jar).d,$(rs_generated_src_jar)) ifneq ($(LOCAL_RENDERSCRIPT_COMPATIBILITY),) ifeq ($(filter $(RSCOMPAT_32BIT_ONLY_API_LEVELS),$(renderscript_target_api)),) ifeq ($(TARGET_IS_64_BIT),true) renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc64/ else renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/bc32/ endif else renderscript_intermediate.bc_folder := $(renderscript_intermediate.COMMON)/res/raw/ endif rs_generated_bc := $(addprefix \ $(renderscript_intermediate.bc_folder), $(bc_files)) renderscript_intermediate := $(intermediates)/renderscript # We don't need the .so files in bundled branches # Prevent these from showing up on the device # One exception is librsjni.so, which is needed for # both native path and compat path. rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni)/librsjni.so LOCAL_JNI_SHARED_LIBRARIES += librsjni ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(FORCE_BUILD_RS_COMPAT)) rs_compatibility_jni_libs := $(addprefix \ $(renderscript_intermediate)/librs., \ $(patsubst %.bc,%.so, $(bc_files))) $(rs_generated_src_jar): .KATI_IMPLICIT_OUTPUTS += $(rs_generated_bc) rs_support_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so LOCAL_JNI_SHARED_LIBRARIES += libRSSupport rs_support_io_lib := # check if the target api level support USAGE_IO ifeq ($(filter $(RSCOMPAT_NO_USAGEIO_API_LEVELS),$(renderscript_target_api)),) rs_support_io_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupportIO)/libRSSupportIO.so LOCAL_JNI_SHARED_LIBRARIES += libRSSupportIO endif my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) ifneq (,$(filter arm64 x86_64,$(my_arch))) my_min_sdk_version := 21 else my_min_sdk_version := $(MIN_SUPPORTED_SDK_VERSION) endif $(rs_compatibility_jni_libs): $(RS_PREBUILT_CLCORE) \ $(rs_support_lib) $(rs_support_io_lib) $(rs_jni_lib) $(rs_compiler_rt) $(rs_compatibility_jni_libs): $(BCC_COMPAT) $(rs_compatibility_jni_libs): PRIVATE_CXX := $(CXX_WRAPPER) $(CLANG_CXX) $(rs_compatibility_jni_libs): PRIVATE_CXX_LINK := $(CLANG_CXX) $(rs_compatibility_jni_libs): PRIVATE_SDK_VERSION := $(my_min_sdk_version) $(rs_compatibility_jni_libs): $(renderscript_intermediate)/librs.%.so: \ $(renderscript_intermediate.bc_folder)%.bc \ $(SOONG_OUT_DIR)/ndk.timestamp $(transform-bc-to-so) endif endif LOCAL_INTERMEDIATE_TARGETS += $(rs_generated_src_jar) # Make sure the generated resource will be added to the apk. LOCAL_RESOURCE_DIR := $(renderscript_intermediate.COMMON)/res $(LOCAL_RESOURCE_DIR) endif ================================================ FILE: core/java_test_config_template.xml ================================================ ================================================ FILE: core/layoutlib_data.mk ================================================ # Data files for layoutlib FONT_TEMP := $(call intermediates-dir-for,PACKAGING,fonts,HOST,COMMON) # The font configuration files - system_fonts.xml, fallback_fonts.xml etc. font_config := $(filter $(TARGET_OUT)/etc/font%.xml, $(INTERNAL_SYSTEMIMAGE_FILES)) font_config := $(addprefix $(FONT_TEMP)/, $(notdir $(font_config))) $(font_config): $(FONT_TEMP)/%: $(TARGET_OUT)/etc/% $(hide) mkdir -p $(dir $@) $(hide) cp -vf $< $@ # List of fonts on the device that we want to ship. This is all .ttf, .ttc and .otf fonts. fonts_device := $(filter $(TARGET_OUT)/fonts/%.ttf $(TARGET_OUT)/fonts/%.ttc $(TARGET_OUT)/fonts/%.otf, $(INTERNAL_SYSTEMIMAGE_FILES)) fonts_device := $(addprefix $(FONT_TEMP)/, $(notdir $(fonts_device))) # TODO: If the font file is a symlink, reuse the font renamed from the symlink # target. $(fonts_device): $(FONT_TEMP)/%: $(TARGET_OUT)/fonts/% $(hide) mkdir -p $(dir $@) $(hide) cp -vf $< $@ KEYBOARD_TEMP := $(call intermediates-dir-for,PACKAGING,keyboards,HOST,COMMON) # The key character map files needed for supporting KeyEvent keyboards := $(sort $(wildcard frameworks/base/data/keyboards/*.kcm)) keyboards := $(addprefix $(KEYBOARD_TEMP)/, $(notdir $(keyboards))) $(keyboards): $(KEYBOARD_TEMP)/%.kcm: frameworks/base/data/keyboards/%.kcm $(hide) mkdir -p $(dir $@) $(hide) cp -vf $< $@ HYPHEN_TEMP := $(call intermediates-dir-for,PACKAGING,hyphen,HOST,COMMON) # The hyphenation pattern files needed to support text hyphenation hyphen := $(filter $(TARGET_OUT)/usr/hyphen-data/%.hyb, $(INTERNAL_SYSTEMIMAGE_FILES)) hyphen := $(addprefix $(HYPHEN_TEMP)/, $(notdir $(hyphen))) $(hyphen): $(HYPHEN_TEMP)/%: $(TARGET_OUT)/usr/hyphen-data/% $(hide) mkdir -p $(dir $@) $(hide) cp -vf $< $@ # List of all data files - font files, font configuration files, key character map files, hyphenation pattern files LAYOUTLIB_FILES := $(fonts_device) $(font_config) $(keyboards) $(hyphen) .PHONY: layoutlib layoutlib-tests layoutlib layoutlib-tests: $(LAYOUTLIB_FILES) $(call dist-for-goals, layoutlib, $(foreach m,$(fonts_device), $(m):layoutlib_native/fonts/$(notdir $(m)))) $(call dist-for-goals, layoutlib, $(foreach m,$(font_config), $(m):layoutlib_native/fonts/$(notdir $(m)))) $(call dist-for-goals, layoutlib, $(foreach m,$(keyboards), $(m):layoutlib_native/keyboards/$(notdir $(m)))) $(call dist-for-goals, layoutlib, $(foreach m,$(hyphen), $(m):layoutlib_native/hyphen-data/$(notdir $(m)))) FONT_TEMP := font_config := fonts_device := FONT_FILES := # The following build process of build.prop, layoutlib-res.zip is moved here from release_layoutlib.sh # so the SBOM of all platform neutral artifacts and Linux/Windows artifacts of layoutlib can be built in Make/Soong. # See go/layoutlib-sbom. # build.prop shipped with layoutlib LAYOUTLIB_BUILD_PROP := $(call intermediates-dir-for,PACKAGING,layoutlib-build-prop,HOST,COMMON) $(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop: $(INSTALLED_SDK_BUILD_PROP_TARGET) rm -rf $@ cp $< $@ # Remove all the uncommon build properties sed -i '/^ro\.\(build\|product\|config\|system\)/!d' $@ # Mark the build as layoutlib. This can be read at runtime by apps sed -i 's|ro.product.brand=generic|ro.product.brand=studio|' $@ sed -i 's|ro.product.device=generic|ro.product.device=layoutlib|' $@ $(call dist-for-goals,layoutlib,$(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop:layoutlib_native/build.prop) # Resource files from frameworks/base/core/res/res LAYOUTLIB_RES := $(call intermediates-dir-for,PACKAGING,layoutlib-res,HOST,COMMON) LAYOUTLIB_RES_FILES := $(shell find frameworks/base/core/res/res -type f -not -path 'frameworks/base/core/res/res/values-m[nc]c*' | sort) EMULATED_OVERLAYS_FILES := $(shell find frameworks/base/packages/overlays/*/res/ | sort) LAYOUTLIB_SUPPORTED_DEVICES := raviole/oriole raviole/raven bluejay/bluejay pantah/panther pantah/cheetah lynx/lynx felix/felix shusky/shiba shusky/husky akita/akita caimito/tokay caimito/caiman caimito/komodo comet/comet tangorpro/tangorpro LAYOUTLIB_DEVICE_OVERLAYS_FILES := $(addsuffix /overlay/frameworks/base/core/res/res/values/*, $(addprefix device/google/, $(LAYOUTLIB_SUPPORTED_DEVICES))) LAYOUTLIB_DEVICE_OVERLAYS_FILES := $(shell find $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) | sort) $(LAYOUTLIB_RES)/layoutlib-res.zip: $(SOONG_ZIP) $(HOST_OUT_EXECUTABLES)/aapt2 $(LAYOUTLIB_RES_FILES) $(EMULATED_OVERLAYS_FILES) $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) frameworks/layoutlib/overlay_codenames.txt rm -rf $@ echo $(LAYOUTLIB_RES_FILES) > $(LAYOUTLIB_RES)/filelist_res.txt $(SOONG_ZIP) -C frameworks/base/core/res -l $(LAYOUTLIB_RES)/filelist_res.txt -o $(LAYOUTLIB_RES)/temp_res.zip echo $(EMULATED_OVERLAYS_FILES) > $(LAYOUTLIB_RES)/filelist_emulated_overlays.txt $(SOONG_ZIP) -C frameworks/base/packages -l $(LAYOUTLIB_RES)/filelist_emulated_overlays.txt -o $(LAYOUTLIB_RES)/temp_emulated_overlays.zip for line in $$(cut -f 1 frameworks/layoutlib/overlay_codenames.txt); \ do splitLine=($${line//:/ }) \ origin_dir=device/google/*/$${splitLine[0]}/overlay/frameworks/base/core/res/res/values; \ target_dir=$(LAYOUTLIB_RES)/overlays/$${splitLine[1]}/res/; \ mkdir -p $$target_dir; \ cp -r $$origin_dir $$target_dir; \ done $(SOONG_ZIP) -C $(LAYOUTLIB_RES) -D $(LAYOUTLIB_RES)/overlays/ -o $(LAYOUTLIB_RES)/temp_device_overlays.zip rm -rf $(LAYOUTLIB_RES)/data && unzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_res.zip unzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_emulated_overlays.zip unzip -q -d $(LAYOUTLIB_RES)/data $(LAYOUTLIB_RES)/temp_device_overlays.zip rm -rf $(LAYOUTLIB_RES)/compiled && mkdir $(LAYOUTLIB_RES)/compiled && $(HOST_OUT_EXECUTABLES)/aapt2 compile $(LAYOUTLIB_RES)/data/res/**/*.9.png -o $(LAYOUTLIB_RES)/compiled printf '\n' > $(LAYOUTLIB_RES)/AndroidManifest.xml $(HOST_OUT_EXECUTABLES)/aapt2 link -R $(LAYOUTLIB_RES)/compiled/* -o $(LAYOUTLIB_RES)/compiled.apk --manifest $(LAYOUTLIB_RES)/AndroidManifest.xml rm -rf $(LAYOUTLIB_RES)/compiled_apk && unzip -q -d $(LAYOUTLIB_RES)/compiled_apk $(LAYOUTLIB_RES)/compiled.apk for f in $(LAYOUTLIB_RES)/compiled_apk/res/*; do mv "$$f" "$${f/-v4/}";done for f in $(LAYOUTLIB_RES)/compiled_apk/res/**/*.9.png; do mv "$$f" "$${f/.9.png/.compiled.9.png}";done cp -r $(LAYOUTLIB_RES)/compiled_apk/res $(LAYOUTLIB_RES)/data $(SOONG_ZIP) -C $(LAYOUTLIB_RES)/data -D $(LAYOUTLIB_RES)/data/ -o $@ $(call dist-for-goals,layoutlib,$(LAYOUTLIB_RES)/layoutlib-res.zip:layoutlib_native/res.zip) # SBOM of layoutlib artifacts LAYOUTLIB_SBOM := $(call intermediates-dir-for,PACKAGING,layoutlib-sbom,HOST) _layoutlib_font_config_files := $(sort $(wildcard frameworks/base/data/fonts/*.xml)) _layoutlib_fonts_files := $(filter $(TARGET_OUT)/fonts/%.ttf $(TARGET_OUT)/fonts/%.ttc $(TARGET_OUT)/fonts/%.otf, $(INTERNAL_SYSTEMIMAGE_FILES)) _layoutlib_keyboard_files := $(sort $(wildcard frameworks/base/data/keyboards/*.kcm)) _layoutlib_hyphen_files := $(filter $(TARGET_OUT)/usr/hyphen-data/%.hyb, $(INTERNAL_SYSTEMIMAGE_FILES)) # Find out files disted with layoutlib in Soong. ### Filter out static libraries for Windows and files already handled in make. _layoutlib_filter_out_disted := $(addprefix layoutlib_native/,fonts/% keyboards/% build.prop res.zip windows/%.a) _layoutlib_files_disted_by_soong := \ $(strip \ $(foreach p,$(_all_dist_src_dst_pairs), \ $(if $(filter-out $(_layoutlib_filter_out_disted),$(filter layoutlib_native/% layoutlib.jar,$(call word-colon,2,$p))),$p))) $(LAYOUTLIB_SBOM)/sbom-metadata.csv: rm -rf $@ echo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $@ echo build.prop,,,,,,Y,$(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop,,, >> $@ $(foreach f,$(_layoutlib_font_config_files),\ echo data/fonts/$(notdir $f),frameworks/base/data/fonts,prebuilt_etc,,,,,$f,,, >> $@; \ ) $(foreach f,$(_layoutlib_fonts_files), \ $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \ $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \ $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \ echo data/fonts/$(notdir $f),$(_module_path),$(_soong_module_type),,,,,$f,,, >> $@; \ ) $(foreach f,$(_layoutlib_keyboard_files), \ echo data/keyboards/$(notdir $f),frameworks/base/data/keyboards,prebuilt_etc,,,,,$f,,, >> $@; \ ) $(foreach f,$(_layoutlib_hyphen_files), \ $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \ $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \ $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \ echo data/hyphen-data/$(notdir $f),$(_module_path),$(_soong_module_type),,,,,$f,,, >> $@; \ ) $(foreach f,$(_layoutlib_files_disted_by_soong), \ $(eval _prebuilt_module_file := $(call word-colon,1,$f)) \ $(eval _dist_file := $(call word-colon,2,$f)) \ $(eval _dist_file := $(patsubst data/windows/%,data/win/lib64/%,$(patsubst layoutlib_native/%,data/%,$(_dist_file)))) \ $(eval _dist_file := $(subst layoutlib.jar,data/layoutlib.jar,$(_dist_file))) \ $(eval _module_name := $(strip $(foreach m,$(ALL_MODULES),$(if $(filter $(_prebuilt_module_file),$(ALL_MODULES.$m.CHECKED)),$m)))) \ $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \ $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \ echo $(patsubst layoutlib_native/%,%,$(_dist_file)),$(_module_path),$(_soong_module_type),,,,,$(_prebuilt_module_file),,, >> $@; \ ) $(foreach f,$(LAYOUTLIB_RES_FILES), \ $(eval _path := $(subst frameworks/base/core/res,data,$f)) \ echo $(_path),,,,,,Y,$f,,, >> $@; \ ) $(foreach f,$(EMULATED_OVERLAYS_FILES), \ $(eval _path := $(subst frameworks/base/packages,data,$f)) \ echo $(_path),,,,,,Y,$f,,, >> $@; \ ) for line in $$(cut -f 1 frameworks/layoutlib/overlay_codenames.txt); do \ splitLine=($${line//:/ }); \ for f in $(LAYOUTLIB_DEVICE_OVERLAYS_FILES); do \ if [[ $$f == */$${splitLine[0]}/* ]]; then \ echo data/overlays/$${splitLine[1]}/res/values/$$(basename $$f),,,,,,Y,$$f,,, >> $@; \ fi \ done \ done .PHONY: layoutlib-sbom layoutlib-sbom: $(LAYOUTLIB_SBOM)/layoutlib.spdx.json $(LAYOUTLIB_SBOM)/layoutlib.spdx.json: $(PRODUCT_OUT)/always_dirty_file.txt $(GEN_SBOM) $(LAYOUTLIB_SBOM)/sbom-metadata.csv $(_layoutlib_font_config_files) $(_layoutlib_fonts_files) $(LAYOUTLIB_BUILD_PROP)/layoutlib-build.prop $(_layoutlib_keyboard_files) $(_layoutlib_hyphen_files) $(LAYOUTLIB_RES_FILES) $(EMULATED_OVERLAYS_FILES) $(LAYOUTLIB_DEVICE_OVERLAYS_FILES) frameworks/layoutlib/overlay_codenames.txt rm -rf $@ $(GEN_SBOM) --output_file $@ --metadata $(LAYOUTLIB_SBOM)/sbom-metadata.csv --build_version $(BUILD_FINGERPRINT_FROM_FILE) --product_mfr "$(PRODUCT_MANUFACTURER)" --module_name "layoutlib" --json $(call dist-for-goals,layoutlib,$(LAYOUTLIB_SBOM)/layoutlib.spdx.json:layoutlib_native/sbom/layoutlib.spdx.json) # Generate SBOM of framework_res.jar that is created in release_layoutlib.sh. # The generated SBOM contains placeholders for release_layoutlib.sh to substitute, and the placeholders include: # document name, document namespace, document creation info, organization and SHA1 value of framework_res.jar. GEN_SBOM_FRAMEWORK_RES := $(HOST_OUT_EXECUTABLES)/generate-sbom-framework_res .PHONY: layoutlib-framework_res-sbom layoutlib-framework_res-sbom: $(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json $(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json: $(LAYOUTLIB_SBOM)/layoutlib.spdx.json $(GEN_SBOM_FRAMEWORK_RES) rm -rf $@ $(GEN_SBOM_FRAMEWORK_RES) --output_file $(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json --layoutlib_sbom $(LAYOUTLIB_SBOM)/layoutlib.spdx.json $(call dist-for-goals,layoutlib,$(LAYOUTLIB_SBOM)/framework_res.jar.spdx.json:layoutlib_native/sbom/framework_res.jar.spdx.json) ================================================ FILE: core/link_type.mk ================================================ # Inputs: # LOCAL_MODULE_CLASS, LOCAL_MODULE, LOCAL_MODULE_MAKEFILE, LOCAL_BUILT_MODULE # from base_rules.mk: my_kind, my_host_cross # my_common: empty or COMMON, like the argument to intermediates-dir-for # my_2nd_arch_prefix: usually LOCAL_2ND_ARCH_VAR_PREFIX, separate for JNI installation # # my_link_type: the tags to apply to this module # my_warn_types: the tags to warn about in our dependencies # my_allowed_types: the tags to allow in our dependencies # my_link_deps: the dependencies, in the form of : # my_link_prefix := LINK_TYPE:$(call find-idf-prefix,$(my_kind),$(my_host_cross)):$(if $(my_common),$(my_common):_,_:$(if $(my_2nd_arch_prefix),$(my_2nd_arch_prefix),_)) link_type := $(my_link_prefix):$(LOCAL_MODULE_CLASS):$(LOCAL_MODULE) ALL_LINK_TYPES += $(link_type) $(link_type).TYPE := $(my_link_type) $(link_type).MAKEFILE := $(LOCAL_MODULE_MAKEFILE) $(link_type).WARN := $(my_warn_types) $(link_type).ALLOWED := $(my_allowed_types) $(link_type).DEPS := $(addprefix $(my_link_prefix):,$(my_link_deps)) $(link_type).BUILT := $(LOCAL_BUILT_MODULE) link_type := my_allowed_types := my_link_prefix := my_link_type := my_warn_types := ================================================ FILE: core/local_current_sdk.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ifdef BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES _override_to := $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES) # b/314011075: apks and jars in the vendor or odm partitions cannot use # system SDK 35 and beyond. In order not to suddenly break those vendor # modules using current or system_current as their LOCAL_SDK_VERSION, # override it to 34, which is the maximum API level allowed for them. ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS))) _override_to := 34 endif ifneq (current,$(_override_to)) ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) ifeq (current,$(LOCAL_SDK_VERSION)) LOCAL_SDK_VERSION := $(_override_to) else ifeq (system_current,$(LOCAL_SDK_VERSION)) LOCAL_SDK_VERSION := system_$(_override_to) endif endif endif _override_to := endif ================================================ FILE: core/local_systemsdk.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ifdef BOARD_SYSTEMSDK_VERSIONS # Apps and jars in vendor, product or odm partition are forced to build against System SDK. _cannot_use_platform_apis := ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already # set correctly before this is included. _cannot_use_platform_apis := true else ifeq ($(LOCAL_PRODUCT_MODULE),true) ifeq ($(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE),true) _cannot_use_platform_apis := true endif endif ifneq (,$(filter JAVA_LIBRARIES APPS,$(LOCAL_MODULE_CLASS))) ifndef LOCAL_SDK_VERSION ifeq ($(_cannot_use_platform_apis),true) ifeq (,$(LOCAL_IS_RUNTIME_RESOURCE_OVERLAY)) # Runtime resource overlays are exempted from building against System SDK. # TODO(b/155027019): remove this, after no product/vendor apps rely on this behavior. LOCAL_SDK_VERSION := system_current # We have run below again since LOCAL_SDK_VERSION is newly set and the "_current" # may have to be updated include $(BUILD_SYSTEM)/local_current_sdk.mk endif endif endif endif endif # Ensure that the selected System SDK version is one of the supported versions. # The range of support versions becomes narrower when BOARD_SYSTEMSDK_VERSIONS # is set, which is a subset of PLATFORM_SYSTEMSDK_VERSIONS. ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) ifneq ($(_cannot_use_platform_apis),true) # apps bundled in system partition can use all system sdk versions provided by the platform _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS) else ifdef BOARD_SYSTEMSDK_VERSIONS # When BOARD_SYSTEMSDK_VERSIONS is set, vendors apps are restricted to use those versions # which is equal to or smaller than PLATFORM_SYSTEMSDK_VERSIONS _supported_systemsdk_versions := $(BOARD_SYSTEMSDK_VERSIONS) else # If not, vendor apks are treated equally to system apps _supported_systemsdk_versions := $(PLATFORM_SYSTEMSDK_VERSIONS) endif # b/314011075: apks and jars in the vendor or odm partitions cannot use system SDK 35 and beyond. # This is to discourage the use of Java APIs in the partitions, which hasn't been supported since # the beginning of the project Treble back in Android 10. Ultimately, we'd like to completely # disallow any Java API in the partitions, but it shall be done progressively. ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) # 28 is the API level when BOARD_SYSTEMSDK_VERSIONS was introduced. So, it's the oldset API # we allow. _supported_systemsdk_versions := $(call int_range_list, 28, 34) endif # Extract version number from LOCAL_SDK_VERSION (ex: system_34 -> 34) _system_sdk_version := $(call get-numeric-sdk-version,$(LOCAL_SDK_VERSION)) # However, the extraction may fail if it doesn't have any number (i.e. current, core_current, # system_current, or similar) Then use the latest platform SDK version number or the actual # codename. ifeq (,$(_system_sdk_version) ifeq (REL,$(PLATFORM_VERSION_CODENAME)) _system_sdk_version := $(PLATFORM_SDK_VERSION) else _system_sdk_version := $(PLATFORM_VERSION_CODENAME) endif endif ifneq ($(_system_sdk_version),$(filter $(_system_sdk_version),$(_supported_systemsdk_versions))) ifneq (true,$(BUILD_BROKEN_DONT_CHECK_SYSTEMSDK) $(call pretty-error,Incompatible LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)'. \ System SDK version '$(_system_sdk_version)' is not supported. Supported versions are: $(_supported_systemsdk_versions)) endif endif _system_sdk_version := _supported_systemsdk_versions := endif ================================================ FILE: core/local_vendor_product.mk ================================================ # LOCAL_USE_VNDK is not the variable which set by module directly, but there are some modules do so. # Set those as LOCAL_IN_VENDOR to make those modules work as expected. ifeq (true,$(LOCAL_USE_VNDK)) $(warning LOCAL_USE_VNDK must not be used. Please use LOCAL_VENDOR_MODULE or LOCAL_PRODUCT_MODULE instead.) LOCAL_IN_VENDOR:=true endif # Set LOCAL_IN_VENDOR for modules going into vendor or odm partition and LOCAL_IN_PRODUCT for product # except for host modules. If LOCAL_SDK_VERSION is set, thats a more restrictive set, so they don't need # LOCAL_IN_VENDOR or LOCAL_IN_PRODUCT ifndef LOCAL_IS_HOST_MODULE ifndef LOCAL_SDK_VERSION ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_OEM_MODULE) $(LOCAL_PROPRIETARY_MODULE))) LOCAL_IN_VENDOR:=true # Note: no need to check LOCAL_MODULE_PATH* since LOCAL_[VENDOR|ODM|OEM]_MODULE is already # set correctly before this is included. endif ifeq (true,$(LOCAL_PRODUCT_MODULE)) LOCAL_IN_PRODUCT:=true endif endif endif ================================================ FILE: core/main.mk ================================================ ifndef KATI $(warning Calling make directly is no longer supported.) $(warning Either use 'envsetup.sh; m' or 'build/soong/soong_ui.bash --make-mode') $(error done) endif $(info [1/1] initializing legacy Make module parser ...) # Absolute path of the present working direcotry. # This overrides the shell variable $PWD, which does not necessarily points to # the top of the source tree, for example when "make -C" is used in m/mm/mmm. PWD := $(shell pwd) # This is the default target. It must be the first declared target. .PHONY: droid DEFAULT_GOAL := droid $(DEFAULT_GOAL): droid_targets .PHONY: droid_targets droid_targets: # Set up various standard variables based on configuration # and host information. include build/make/core/config.mk ifneq ($(filter $(dont_bother_goals), $(MAKECMDGOALS)),) dont_bother := true endif .KATI_READONLY := SOONG_CONFIG_NAMESPACES .KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),SOONG_CONFIG_$(n)) .KATI_READONLY := $(foreach n,$(SOONG_CONFIG_NAMESPACES),$(foreach k,$(SOONG_CONFIG_$(n)),SOONG_CONFIG_$(n)_$(k))) include $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk YACC :=$= $(BISON) -d include $(BUILD_SYSTEM)/clang/config.mk # Write the build number to a file so it can be read back in # without changing the command line every time. Avoids rebuilds # when using ninja. BUILD_NUMBER_FILE := $(SOONG_OUT_DIR)/build_number.txt $(KATI_obsolete_var BUILD_NUMBER,See https://android.googlesource.com/platform/build/+/master/Changes.md#BUILD_NUMBER) BUILD_HOSTNAME_FILE := $(SOONG_OUT_DIR)/build_hostname.txt $(KATI_obsolete_var BUILD_HOSTNAME,Use BUILD_HOSTNAME_FROM_FILE instead) $(KATI_obsolete_var FILE_NAME_TAG,https://android.googlesource.com/platform/build/+/master/Changes.md#FILE_NAME_TAG) .KATI_RESTAT: $(BUILD_NUMBER_FILE) .KATI_RESTAT: $(BUILD_HOSTNAME_FILE) DATE_FROM_FILE := date -d @$(BUILD_DATETIME_FROM_FILE) .KATI_READONLY := DATE_FROM_FILE # Make an empty directory, which can be used to make empty jars EMPTY_DIRECTORY := $(OUT_DIR)/empty $(shell mkdir -p $(EMPTY_DIRECTORY) && rm -rf $(EMPTY_DIRECTORY)/*) # CTS-specific config. -include cts/build/config.mk # device-tests-specific-config. -include tools/tradefederation/build/suites/device-tests/config.mk # general-tests-specific-config. -include tools/tradefederation/build/suites/general-tests/config.mk # STS-specific config. -include test/sts/tools/sts-tradefed/build/config.mk # CTS-Instant-specific config -include test/suite_harness/tools/cts-instant-tradefed/build/config.mk # MTS-specific config. -include test/mts/tools/build/config.mk # VTS-Core-specific config. -include test/vts/tools/vts-core-tradefed/build/config.mk # CSUITE-specific config. -include test/app_compat/csuite/tools/build/config.mk # CATBox-specific config. -include test/catbox/tools/build/config.mk # CTS-Root-specific config. -include test/cts-root/tools/build/config.mk # WVTS-specific config. -include test/wvts/tools/build/config.mk # DTS-specific config. -include test/dts/tools/build/config.mk # Clean rules .PHONY: clean-dex-files clean-dex-files: $(hide) find $(OUT_DIR) -name "*.dex" | xargs rm -f $(hide) for i in `find $(OUT_DIR) -name "*.jar" -o -name "*.apk"` ; do ((unzip -l $$i 2> /dev/null | \ grep -q "\.dex$$" && rm -f $$i) || continue ) ; done @echo "All dex files and archives containing dex files have been removed." # Include the google-specific config -include vendor/google/build/config.mk # These are the modifier targets that don't do anything themselves, but # change the behavior of the build. # (must be defined before including definitions.make) INTERNAL_MODIFIER_TARGETS := all # EMMA_INSTRUMENT_STATIC merges the static jacoco library to each # jacoco-enabled module. ifeq (true,$(EMMA_INSTRUMENT_STATIC)) EMMA_INSTRUMENT := true endif ifdef TARGET_ARCH_SUITE # TODO(b/175577370): Enable this error. # $(error TARGET_ARCH_SUITE is not supported in kati/make builds) endif $(KATI_obsolete_var ADDITIONAL_BUILD_PROPERTIES, Please use ADDITIONAL_SYSTEM_PROPERTIES) # Bring in standard build system definitions. include $(BUILD_SYSTEM)/definitions.mk ifneq ($(filter user userdebug eng,$(MAKECMDGOALS)),) $(info ***************************************************************) $(info ***************************************************************) $(info Do not pass '$(filter user userdebug eng,$(MAKECMDGOALS))' on \ the make command line.) $(info Set TARGET_BUILD_VARIANT in buildspec.mk, or use lunch or) $(info choosecombo.) $(info ***************************************************************) $(info ***************************************************************) $(error stopping) endif # These are the valid values of TARGET_BUILD_VARIANT. INTERNAL_VALID_VARIANTS := user userdebug eng ifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),) $(info ***************************************************************) $(info ***************************************************************) $(info Invalid variant: $(TARGET_BUILD_VARIANT)) $(info Valid values are: $(INTERNAL_VALID_VARIANTS)) $(info ***************************************************************) $(info ***************************************************************) $(error stopping) endif # ----------------------------------------------------------------- # PDK builds are no longer supported, this is always platform TARGET_BUILD_JAVA_SUPPORT_LEVEL :=$= platform $(KATI_obsolete_var PRODUCT_FULL_TREBLE,\ Code should be written to work regardless of a device being Treble) # ----------------------------------------------------------------- ### ### In this section we set up the things that are different ### between the build variants ### is_sdk_build := ifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),) is_sdk_build := true endif tags_to_install := ifeq ($(TARGET_BUILD_VARIANT),userdebug) # Pick up some extra useful tools tags_to_install := debug endif ifeq ($(TARGET_BUILD_VARIANT),eng) tags_to_install := debug eng endif ## asan ## # Install some additional tools on ASAN builds IFF we are also installing debug tools ifneq ($(filter address,$(SANITIZE_TARGET)),) ifneq (,$(filter debug,$(tags_to_install))) tags_to_install += asan endif endif ## java coverage ## # Install additional tools on java coverage builds ifeq (true,$(EMMA_INSTRUMENT)) ifneq (,$(filter debug,$(tags_to_install))) tags_to_install += java_coverage endif endif ## sdk ## ifdef is_sdk_build # Detect if we want to build a repository for the SDK sdk_repo_goal := $(strip $(filter sdk_repo,$(MAKECMDGOALS))) MAKECMDGOALS := $(strip $(filter-out sdk_repo,$(MAKECMDGOALS))) ifneq ($(words $(sort $(filter-out $(INTERNAL_MODIFIER_TARGETS) checkbuild emulator_tests,$(MAKECMDGOALS)))),1) $(error The 'sdk' target may not be specified with any other targets) endif # TODO: this should be eng I think. Since the sdk is built from the eng # variant. tags_to_install := debug eng else # !sdk endif BUILD_WITHOUT_PV := true # ------------------------------------------------------------ # Define a function that, given a list of module tags, returns # non-empty if that module should be installed in /system. # For most goals, anything not tagged with the "tests" tag should # be installed in /system. define should-install-to-system $(if $(filter tests,$(1)),,true) endef ifdef is_sdk_build # For the sdk goal, anything with the "samples" tag should be # installed in /data even if that module also has "eng"/"debug"/"user". define should-install-to-system $(if $(filter samples tests,$(1)),,true) endef endif # If they only used the modifier goals (all, etc), we'll actually # build the default target. ifeq ($(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS)),) .PHONY: $(INTERNAL_MODIFIER_TARGETS) $(INTERNAL_MODIFIER_TARGETS): $(DEFAULT_GOAL) endif # # Typical build; include any Android.mk files we can find. # include $(BUILD_SYSTEM)/art_config.mk # Bring in dex_preopt.mk # This creates some modules so it needs to be included after # should-install-to-system is defined (in order for base_rules.mk to function # properly), but before readonly-final-product-vars is called. include $(BUILD_SYSTEM)/dex_preopt.mk # Strip and readonly a few more variables so they won't be modified. $(readonly-final-product-vars) ifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),) ENFORCE_RRO_SOURCES := endif # Color-coded warnings including current module info # $(1): message to print define pretty-warning $(shell $(call echo-warning,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1))) endef # Color-coded errors including current module info # $(1): message to print define pretty-error $(shell $(call echo-error,$(LOCAL_MODULE_MAKEFILE),$(LOCAL_MODULE): $(1))) $(error done) endef subdir_makefiles_inc := . FULL_BUILD := ifneq ($(dont_bother),true) FULL_BUILD := true # # Include all of the makefiles in the system # subdir_makefiles := \ $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk \ $(SOONG_ANDROID_MK) \ build/make/target/board/android-info.mk # Android.mk files are only used on Linux builds, Mac only supports Android.bp ifeq ($(HOST_OS),linux) ifeq ($(PRODUCT_IGNORE_ALL_ANDROIDMK),true) allowed_androidmk_files := ifdef PRODUCT_ANDROIDMK_ALLOWLIST_FILE -include $(PRODUCT_ANDROIDMK_ALLOWLIST_FILE) endif allowed_androidmk_files += $(PRODUCT_ALLOWED_ANDROIDMK_FILES) subdir_makefiles += $(filter $(allowed_androidmk_files),$(file <$(OUT_DIR)/.module_paths/Android.mk.list)) allowed_androidmk_files := else subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list) endif endif subdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk subdir_makefiles_total := $(words int $(subdir_makefiles) post finish) .KATI_READONLY := subdir_makefiles_total $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk))) -include device/generic/goldfish/tasks/emu_img_zip.mk # Build bootloader.img/radio.img, and unpack the partitions. -include vendor/google_devices/$(TARGET_SOC)/prebuilts/misc_bins/update_bootloader_radio_image.mk # For an unbundled image, we can skip blueprint_tools because unbundled image # aims to remove a large number framework projects from the manifest, the # sources or dependencies for these tools may be missing from the tree. ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE)) droid_targets : blueprint_tools checkbuild: blueprint_tests endif # Create necessary directories and symlinks in the root filesystem include system/core/rootdir/create_root_structure.mk endif # dont_bother ifndef subdir_makefiles_total subdir_makefiles_total := $(words init post finish) endif $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] finishing legacy Make module parsing ...) # ------------------------------------------------------------------- # All module makefiles have been included at this point. # ------------------------------------------------------------------- # ------------------------------------------------------------------- # Use basic warning/error messages now that LOCAL_MODULE_MAKEFILE # and LOCAL_MODULE aren't useful anymore. # ------------------------------------------------------------------- define pretty-warning $(warning $(1)) endef define pretty-error $(error $(1)) endef # ------------------------------------------------------------------- # Enforce to generate all RRO packages for modules having resource # overlays. # ------------------------------------------------------------------- ifneq ($(PRODUCT_ENFORCE_RRO_TARGETS),) $(call generate_all_enforce_rro_packages) endif # ------------------------------------------------------------------- # Sort ALL_MODULES to remove duplicate entries. # ------------------------------------------------------------------- ALL_MODULES := $(sort $(ALL_MODULES)) # Cannot set to readonly because Makefile extends ALL_MODULES # .KATI_READONLY := ALL_MODULES # ------------------------------------------------------------------- # Fix up CUSTOM_MODULES to refer to installed files rather than # just bare module names. Leave unknown modules alone in case # they're actually full paths to a particular file. known_custom_modules := $(filter $(ALL_MODULES),$(CUSTOM_MODULES)) unknown_custom_modules := $(filter-out $(ALL_MODULES),$(CUSTOM_MODULES)) CUSTOM_MODULES := \ $(call module-installed-files,$(known_custom_modules)) \ $(unknown_custom_modules) # ------------------------------------------------------------------- # Define dependencies for modules that require other modules. # This can only happen now, after we've read in all module makefiles. # # TODO: deal with the fact that a bare module name isn't # unambiguous enough. Maybe declare short targets like # APPS:Quake or HOST:SHARED_LIBRARIES:libutils. # BUG: the system image won't know to depend on modules that are # brought in as requirements of other modules. # # Resolve the required module name to 32-bit or 64-bit variant. # Get a list of corresponding module names for the second arch, if they exist. # $(1): TARGET, HOST or HOST_CROSS # $(2): A list of module names define get-modules-for-2nd-arch $(strip \ $(foreach m,$(2), \ $(if $(filter true,$(ALL_MODULES.$(m)$($(1)_2ND_ARCH_MODULE_SUFFIX).FOR_2ND_ARCH)), \ $(m)$($(1)_2ND_ARCH_MODULE_SUFFIX) \ ) \ ) \ ) endef # Resolves module bitness for PRODUCT_PACKAGES and PRODUCT_HOST_PACKAGES. # The returned list of module names can be used to access # ALL_MODULES..<*> variables. # Name resolution for PRODUCT_PACKAGES / PRODUCT_HOST_PACKAGES: # foo:32 resolves to foo_32; # foo:64 resolves to foo; # foo resolves to both foo and foo_32 (if foo_32 is defined). # # Name resolution for HOST_CROSS modules: # foo:32 resolves to foo; # foo:64 resolves to foo_64; # foo resolves to both foo and foo_64 (if foo_64 is defined). # # $(1): TARGET, HOST or HOST_CROSS # $(2): A list of simple module names with :32 and :64 suffix define resolve-bitness-for-modules $(strip \ $(eval modules_32 := $(patsubst %:32,%,$(filter %:32,$(2)))) \ $(eval modules_64 := $(patsubst %:64,%,$(filter %:64,$(2)))) \ $(eval modules_both := $(filter-out %:32 %:64,$(2))) \ $(eval ### if 2ND_HOST_CROSS_IS_64_BIT, then primary/secondary are reversed for HOST_CROSS modules) \ $(if $(filter HOST_CROSS_true,$(1)_$(2ND_HOST_CROSS_IS_64_BIT)), \ $(eval modules_1st_arch := $(modules_32)) \ $(eval modules_2nd_arch := $(modules_64)), \ $(eval modules_1st_arch := $(modules_64)) \ $(eval modules_2nd_arch := $(modules_32))) \ $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \ $(eval modules := $(modules_1st_arch)) \ $(if $($(1)_2ND_ARCH), \ $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_2nd_arch))), \ $(eval modules += $(modules_2nd_arch))) \ $(eval ### For the rest we add both) \ $(eval modules += $(modules_both)) \ $(if $($(1)_2ND_ARCH), \ $(eval modules += $(call get-modules-for-2nd-arch,$(1),$(modules_both)))) \ $(modules) \ ) endef # Resolve the required module names to 32-bit or 64-bit variant for: # ALL_MODULES.<*>.REQUIRED_FROM_TARGET # ALL_MODULES.<*>.REQUIRED_FROM_HOST # ALL_MODULES.<*>.REQUIRED_FROM_HOST_CROSS # # If a module is for cross host OS, the required modules are also for that OS. # Required modules explicitly suffixed with :32 or :64 resolve to that bitness. # Otherwise if the requiring module is native and the required module is shared # library or native test, then the required module resolves to the same bitness. # Otherwise the required module resolves to both variants, if they exist. # $(1): TARGET, HOST or HOST_CROSS define select-bitness-of-required-modules $(foreach m,$(ALL_MODULES), \ $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_$(1))) \ $(if $(r), \ $(if $(filter HOST_CROSS,$(1)), \ $(if $(ALL_MODULES.$(m).FOR_HOST_CROSS),,$(error Only expected REQUIRED_FROM_HOST_CROSS on FOR_HOST_CROSS modules - $(m))) \ $(eval r := $(addprefix host_cross_,$(r)))) \ $(eval module_is_native := \ $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(ALL_MODULES.$(m).CLASS))) \ $(eval r_r := \ $(foreach r_i,$(r), \ $(if $(filter %:32 %:64,$(r_i)), \ $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))), \ $(eval r_m := \ $(eval r_i_2nd := $(call get-modules-for-2nd-arch,$(1),$(r_i))) \ $(eval required_is_shared_library_or_native_test := \ $(filter SHARED_LIBRARIES NATIVE_TESTS, \ $(ALL_MODULES.$(r_i).CLASS) $(ALL_MODULES.$(r_i_2nd).CLASS))) \ $(if $(and $(module_is_native),$(required_is_shared_library_or_native_test)), \ $(if $(ALL_MODULES.$(m).FOR_2ND_ARCH),$(r_i_2nd),$(r_i)), \ $(r_i) $(r_i_2nd)))) \ $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \ $(if $(r_m),,$(eval _nonexistent_required += $(1)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \ $(r_m))) \ $(eval ALL_MODULES.$(m).REQUIRED_FROM_$(1) := $(sort $(r_r))) \ ) \ ) endef # Resolve the required module names to 32-bit or 64-bit variant for: # ALL_MODULES.<*>.TARGET_REQUIRED_FROM_HOST # ALL_MODULES.<*>.HOST_REQUIRED_FROM_TARGET # # This is like select-bitness-of-required-modules, but it doesn't have # complicated logic for various module types. # Calls resolve-bitness-for-modules to resolve module names. # $(1): TARGET or HOST # $(2): HOST or TARGET define select-bitness-of-target-host-required-modules $(foreach m,$(ALL_MODULES), \ $(eval r := $(ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2))) \ $(if $(r), \ $(eval r_r := \ $(foreach r_i,$(r), \ $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))) \ $(eval r_m := $(foreach r_j,$(r_m),$(if $(ALL_MODULES.$(r_j).PATH),$(r_j)))) \ $(if $(r_m),,$(eval _nonexistent_required += $(2)$(comma)$(m)$(comma)$(1)$(comma)$(r_i))) \ $(r_m))) \ $(eval ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2) := $(sort $(r_r))) \ ) \ ) endef _nonexistent_required := $(call select-bitness-of-required-modules,TARGET) $(call select-bitness-of-required-modules,HOST) $(call select-bitness-of-required-modules,HOST_CROSS) $(call select-bitness-of-target-host-required-modules,TARGET,HOST) $(call select-bitness-of-target-host-required-modules,HOST,TARGET) _nonexistent_required := $(sort $(_nonexistent_required)) check_missing_required_modules := true ifneq (,$(filter true,$(ALLOW_MISSING_DEPENDENCIES) $(BUILD_BROKEN_MISSING_REQUIRED_MODULES))) check_missing_required_modules := endif # ALLOW_MISSING_DEPENDENCIES == true || BUILD_BROKEN_MISSING_REQUIRED_MODULES == true # Some executables are skipped in ASAN SANITIZE_TARGET build, thus breaking their dependencies. ifneq (,$(filter address,$(SANITIZE_TARGET))) check_missing_required_modules := endif # SANITIZE_TARGET has ASAN # HOST OS darwin build is broken, disable this check for darwin for now. # TODO(b/162102724): Remove this when darwin host has no broken dependency. ifneq (,$(filter $(HOST_OS),darwin)) check_missing_required_modules := endif # HOST_OS == darwin ifeq (true,$(check_missing_required_modules)) ifneq (,$(_nonexistent_required)) $(warning Missing required dependencies:) $(foreach r_i,$(_nonexistent_required), \ $(eval r := $(subst $(comma),$(space),$(r_i))) \ $(info $(word 1,$(r)) module $(word 2,$(r)) requires non-existent $(word 3,$(r)) module: $(word 4,$(r))) \ ) $(warning Set BUILD_BROKEN_MISSING_REQUIRED_MODULES := true to bypass this check if this is intentional) ifneq (,$(PRODUCT_SOURCE_ROOT_DIRS)) $(warning PRODUCT_SOURCE_ROOT_DIRS is non-empty. Some necessary modules may have been skipped by Soong) endif $(error Build failed) endif # _nonexistent_required != empty endif # check_missing_required_modules == true define add-required-deps $(1): | $(2) endef # Use a normal dependency instead of an order-only dependency when installing # host dynamic binaries so that the timestamp of the final binary always # changes, even if the toc optimization has skipped relinking the binary # and its dependant shared libraries. define add-required-host-so-deps $(1): $(2) endef # Sets up dependencies such that whenever a host module is installed, # any other host modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST) will also be installed define add-all-host-to-host-required-modules-deps $(foreach m,$(ALL_MODULES), \ $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)) \ $(if $(r), \ $(eval r := $(call module-installed-files,$(r))) \ $(eval h_m := $(filter $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ $(eval h_r := $(filter $(HOST_OUT)/%, $(r))) \ $(eval h_r := $(filter-out $(h_m), $(h_r))) \ $(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \ ) \ ) endef $(call add-all-host-to-host-required-modules-deps) # Sets up dependencies such that whenever a host cross module is installed, # any other host cross modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS) will also be installed define add-all-host-cross-to-host-cross-required-modules-deps $(foreach m,$(ALL_MODULES), \ $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_HOST_CROSS)) \ $(if $(r), \ $(eval r := $(call module-installed-files,$(r))) \ $(eval hc_m := $(filter $(HOST_CROSS_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ $(eval hc_r := $(filter $(HOST_CROSS_OUT)/%, $(r))) \ $(eval hc_r := $(filter-out $(hc_m), $(hc_r))) \ $(if $(hc_m), $(eval $(call add-required-deps, $(hc_m),$(hc_r)))) \ ) \ ) endef $(call add-all-host-cross-to-host-cross-required-modules-deps) # Sets up dependencies such that whenever a target module is installed, # any other target modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET) will also be installed # This doesn't apply to ORDERONLY_INSTALLED items. define add-all-target-to-target-required-modules-deps $(foreach m,$(ALL_MODULES), \ $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET)) \ $(if $(r), \ $(eval r := $(call module-installed-files,$(r))) \ $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \ $(eval t_m := $(filter-out $(ALL_MODULES.$(m).ORDERONLY_INSTALLED), $(ALL_MODULES.$(m).INSTALLED))) \ $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \ $(eval t_r := $(filter-out $(t_m), $(t_r))) \ $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \ ) \ ) endef $(call add-all-target-to-target-required-modules-deps) # Sets up dependencies such that whenever a host module is installed, # any target modules listed in $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST) will also be installed define add-all-host-to-target-required-modules-deps $(foreach m,$(ALL_MODULES), \ $(eval req_mods := $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))\ $(if $(req_mods), \ $(eval req_files := )\ $(foreach req_mod,$(req_mods), \ $(eval req_file := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(req_mod)))) \ $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \ , \ $(if $(strip $(req_file)), \ , \ $(error $(m).LOCAL_TARGET_REQUIRED_MODULES : illegal value $(req_mod) : not a device module. If you want to specify host modules to be required to be installed along with your host module, add those module names to LOCAL_REQUIRED_MODULES instead) \ ) \ ) \ $(eval req_files := $(req_files)$(space)$(req_file))\ )\ $(eval req_files := $(strip $(req_files)))\ $(eval mod_files := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(m)))) \ $(if $(mod_files),\ $(eval $(call add-required-deps, $(mod_files),$(req_files))) \ )\ )\ ) endef $(call add-all-host-to-target-required-modules-deps) # Sets up dependencies such that whenever a target module is installed, # any host modules listed in $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET) will also be installed define add-all-target-to-host-required-modules-deps $(foreach m,$(ALL_MODULES), \ $(eval req_mods := $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))\ $(if $(req_mods), \ $(eval req_files := )\ $(foreach req_mod,$(req_mods), \ $(eval req_file := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(req_mod)))) \ $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \ , \ $(if $(strip $(req_file)), \ , \ $(error $(m).LOCAL_HOST_REQUIRED_MODULES : illegal value $(req_mod) : not a host module. If you want to specify target modules to be required to be installed along with your target module, add those module names to LOCAL_REQUIRED_MODULES instead) \ ) \ ) \ $(eval req_files := $(req_files)$(space)$(req_file))\ )\ $(eval req_files := $(strip $(req_files)))\ $(eval mod_files := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(m))))\ $(if $(mod_files),\ $(eval $(call add-required-deps, $(mod_files),$(req_files))) \ )\ )\ ) endef $(call add-all-target-to-host-required-modules-deps) t_m := h_m := hc_m := t_r := h_r := hc_r := # Establish the dependencies on the shared libraries. # It also adds the shared library module names to ALL_MODULES.$(m).REQUIRED_FROM_(TARGET|HOST|HOST_CROSS), # so they can be expanded to product_MODULES later. # $(1): TARGET_ or HOST_ or HOST_CROSS_. # $(2): non-empty for 2nd arch. # $(3): non-empty for host cross compile. define resolve-shared-libs-depes $(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\ $(eval p := $(subst :,$(space),$(m)))\ $(eval mod := $(firstword $(p)))\ $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\ $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\ $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\ $(if $(3),$(eval deps := $(addprefix host_cross_,$(deps))))\ $(eval r := $(filter $($(root))/%,$(call module-installed-files,\ $(deps))))\ $(if $(filter $(1),HOST_),\ $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARY_FILES) $(word 2,$(p)) $(r))\ $(eval ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES := $$(ALL_MODULES.$(mod).HOST_SHARED_LIBRARIES) $(deps))\ $(eval $(call add-required-host-so-deps,$(word 2,$(p)),$(r))),\ $(eval $(call add-required-deps,$(word 2,$(p)),$(r))))\ $(eval ALL_MODULES.$(mod).REQUIRED_FROM_$(patsubst %_,%,$(1)) += $(deps))) endef # Recursively resolve host shared library dependency for a given module. # $(1): module name # Returns all dependencies of shared library. define get-all-shared-libs-deps $(if $(_all_deps_for_$(1)_set_),$(_all_deps_for_$(1)_),\ $(eval _all_deps_for_$(1)_ :=) \ $(foreach dep,$(ALL_MODULES.$(1).HOST_SHARED_LIBRARIES),\ $(foreach m,$(call get-all-shared-libs-deps,$(dep)),\ $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(m))\ $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_))))\ $(eval _all_deps_for_$(1)_ := $$(_all_deps_for_$(1)_) $(dep))\ $(eval _all_deps_for_$(1)_ := $(sort $(_all_deps_for_$(1)_) $(dep)))\ $(eval _all_deps_for_$(1)_set_ := true))\ $(_all_deps_for_$(1)_)) endef # Scan all modules in general-tests, device-tests and other selected suites and # flatten the shared library dependencies. define update-host-shared-libs-deps-for-suites $(foreach suite,general-tests device-tests vts tvts art-host-tests host-unit-tests camera-hal-tests,\ $(foreach m,$(COMPATIBILITY.$(suite).MODULES),\ $(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\ $(foreach dep,$(my_deps),\ $(foreach f,$(ALL_MODULES.$(dep).HOST_SHARED_LIBRARY_FILES),\ $(if $(filter $(suite),device-tests general-tests art-host-tests host-unit-tests camera-hal-tests),\ $(eval my_testcases := $(HOST_OUT_TESTCASES)),\ $(eval my_testcases := $$(COMPATIBILITY_TESTCASES_OUT_$(suite))))\ $(eval target := $(my_testcases)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\ $(eval prefix := ../../..) $(if $(strip $(patsubst %x86,,$(COMPATIBILITY.$(suite).ARCH_DIRS.$(m)))), \ $(if $(strip $(patsubst %x86_64,,$(COMPATIBILITY.$(suite).ARCH_DIRS.$(m)))),$(eval prefix := ../..),),) \ $(eval link_target := $(prefix)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\ $(eval symlink := $(COMPATIBILITY.$(suite).ARCH_DIRS.$(m))/shared_libs/$(notdir $(f)))\ $(eval COMPATIBILITY.$(suite).SYMLINKS := \ $$(COMPATIBILITY.$(suite).SYMLINKS) $(f):$(link_target):$(symlink))\ $(if $(strip $(ALL_TARGETS.$(target).META_LIC)),,$(call declare-copy-target-license-metadata,$(target),$(f)))\ $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \ $$(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES) $(f):$(target))\ $(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \ $(sort $(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES))))))\ $(eval COMPATIBILITY.$(suite).SYMLINKS := $(sort $(COMPATIBILITY.$(suite).SYMLINKS)))) endef $(call resolve-shared-libs-depes,TARGET_) ifdef TARGET_2ND_ARCH $(call resolve-shared-libs-depes,TARGET_,true) endif $(call resolve-shared-libs-depes,HOST_) ifdef HOST_2ND_ARCH $(call resolve-shared-libs-depes,HOST_,true) endif # Update host side shared library dependencies for tests in suite device-tests and general-tests. # This should be called after calling resolve-shared-libs-depes for HOST_2ND_ARCH. $(call update-host-shared-libs-deps-for-suites) ifdef HOST_CROSS_OS $(call resolve-shared-libs-depes,HOST_CROSS_,,true) ifdef HOST_CROSS_2ND_ARCH $(call resolve-shared-libs-depes,HOST_CROSS_,true,true) endif endif # Pass the shared libraries dependencies to prebuilt ELF file check. define add-elf-file-check-shared-lib $(1): PRIVATE_SHARED_LIBRARY_FILES += $(2) $(1): $(2) endef define resolve-shared-libs-for-elf-file-check $(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\ $(eval p := $(subst :,$(space),$(m)))\ $(eval mod := $(firstword $(p)))\ \ $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\ $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\ $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\ $(eval deps := $(filter $($(root))/%$($(1)SHLIB_SUFFIX),$(call module-built-files,$(deps))))\ \ $(eval r := $(firstword $(filter \ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/EXECUTABLES/%\ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/NATIVE_TESTS/%\ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/SHARED_LIBRARIES/%,\ $(call module-built-files,$(mod)))))\ \ $(if $(and $(r),$(deps)),\ $(eval stamp := $(dir $(r))check_elf_files.timestamp)\ $(if $(CHECK_ELF_FILES.$(stamp)),\ $(eval $(call add-elf-file-check-shared-lib,$(stamp),$(deps))))\ )) endef $(call resolve-shared-libs-for-elf-file-check,TARGET_) ifdef TARGET_2ND_ARCH $(call resolve-shared-libs-for-elf-file-check,TARGET_,true) endif m := r := p := stamp := deps := add-required-deps := ################################################################################ # Link type checking # # ALL_LINK_TYPES contains a list of all link type prefixes (generally one per # module, but APKs can "link" to both java and native code). The link type # prefix consists of all the information needed by intermediates-dir-for: # # LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo # # 1: LINK_TYPE literal # 2: prefix # - TARGET # - HOST # - HOST_CROSS # 3: Whether to use the common intermediates directory or not # - _ # - COMMON # 4: Whether it's the second arch or not # - _ # - 2ND_ # 5: Module Class # - STATIC_LIBRARIES # - SHARED_LIBRARIES # - ... # 6: Module Name # # Then fields under that are separated by a period and the field name: # - TYPE: the link types for this module # - MAKEFILE: Where this module was defined # - BUILT: The built module location # - DEPS: the link type prefixes for the module's dependencies # - ALLOWED: the link types to allow in this module's dependencies # - WARN: the link types to warn about in this module's dependencies # # All of the dependency link types not listed in ALLOWED or WARN will become # errors. ################################################################################ link_type_error := define link-type-prefix $(word 2,$(subst :,$(space),$(1))) endef define link-type-common $(patsubst _,,$(word 3,$(subst :,$(space),$(1)))) endef define link-type-2ndarchprefix $(patsubst _,,$(word 4,$(subst :,$(space),$(1)))) endef define link-type-class $(word 5,$(subst :,$(space),$(1))) endef define link-type-name $(word 6,$(subst :,$(space),$(1))) endef define link-type-os $(strip $(eval _p := $(link-type-prefix))\ $(if $(filter HOST HOST_CROSS,$(_p)),\ $($(_p)_OS),\ android)) endef define link-type-arch $($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH) endef define link-type-name-variant $(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch)) endef # $(1): the prefix of the module doing the linking # $(2): the prefix of the linked module define link-type-warning $(shell $(call echo-warning,$($(1).MAKEFILE),"$(call link-type-name,$(1)) ($($(1).TYPE)) should not link against $(call link-type-name,$(2)) ($(3))")) endef # $(1): the prefix of the module doing the linking # $(2): the prefix of the linked module define link-type-error $(shell $(call echo-error,$($(1).MAKEFILE),"$(call link-type-name,$(1)) ($($(1).TYPE)) can not link against $(call link-type-name,$(2)) ($(3))"))\ $(eval link_type_error := true) endef link-type-missing := ifneq ($(ALLOW_MISSING_DEPENDENCIES),true) # Print an error message if the linked-to module is missing # $(1): the prefix of the module doing the linking # $(2): the prefix of the missing module define link-type-missing $(shell $(call echo-error,$($(1).MAKEFILE),"$(call link-type-name-variant,$(1)) missing $(call link-type-name-variant,$(2))"))\ $(eval available_variants := $(filter %:$(call link-type-name,$(2)),$(ALL_LINK_TYPES)))\ $(if $(available_variants),\ $(info Available variants:)\ $(foreach v,$(available_variants),$(info $(space)$(space)$(call link-type-name-variant,$(v)))))\ $(info You can set ALLOW_MISSING_DEPENDENCIES=true in your environment if this is intentional, but that may defer real problems until later in the build.)\ $(eval link_type_error := true) endef else define link-type-missing $(eval $$(1).MISSING := true) endef endif # Verify that $(1) can link against $(2) # Both $(1) and $(2) are the link type prefix defined above define verify-link-type $(foreach t,$($(2).TYPE),\ $(if $(filter-out $($(1).ALLOWED),$(t)),\ $(if $(filter $(t),$($(1).WARN)),\ $(call link-type-warning,$(1),$(2),$(t)),\ $(call link-type-error,$(1),$(2),$(t))))) endef $(foreach lt,$(ALL_LINK_TYPES),\ $(foreach d,$($(lt).DEPS),\ $(if $($(d).TYPE),\ $(call verify-link-type,$(lt),$(d)),\ $(call link-type-missing,$(lt),$(d))))) ifdef link_type_error $(error exiting from previous errors) endif # ------------------------------------------------------------------- # Handle exported/imported includes # Recursively calculate flags $(foreach export,$(EXPORTS_LIST), \ $(eval EXPORTS.$$(export) = $$(EXPORTS.$(export).FLAGS) \ $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORTS.$(dep))))) # Recursively calculate dependencies $(foreach export,$(EXPORTS_LIST), \ $(eval EXPORT_DEPS.$$(export) = $$(EXPORTS.$(export).DEPS) \ $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORT_DEPS.$(dep))))) # Converts the recursive variables to simple variables so that we don't have to # evaluate them for every .o rule $(foreach export,$(EXPORTS_LIST),$(eval EXPORTS.$$(export) := $$(strip $$(EXPORTS.$$(export))))) $(foreach export,$(EXPORTS_LIST),$(eval EXPORT_DEPS.$$(export) := $$(sort $$(EXPORT_DEPS.$$(export))))) # Add dependencies $(foreach export,$(EXPORTS_LIST),$(eval $(call add-dependency,$$(EXPORTS.$$(export).USERS),$$(EXPORT_DEPS.$$(export))))) # ------------------------------------------------------------------- # Figure out our module sets. # # Of the modules defined by the component makefiles, # determine what we actually want to build. # Expand a list of modules to the modules that they override (if any) # $(1): The list of modules. define module-overrides $(foreach m,$(1),\ $(eval _mo_overrides := $(PACKAGES.$(m).OVERRIDES) $(EXECUTABLES.$(m).OVERRIDES) $(SHARED_LIBRARIES.$(m).OVERRIDES) $(ETC.$(m).OVERRIDES))\ $(if $(filter $(m),$(_mo_overrides)),\ $(error Module $(m) cannot override itself),\ $(_mo_overrides))) endef ########################################################### ## Expand a module name list with REQUIRED modules ########################################################### # $(1): The variable name that holds the initial module name list. # the variable will be modified to hold the expanded results. # $(2): The initial module name list. # $(3): The list of overridden modules. # Returns empty string (maybe with some whitespaces). define expand-required-modules $(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))) \ $(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \ $(eval _erm_new_overrides := $(call module-overrides,$(_erm_new_modules))) \ $(eval _erm_all_overrides := $(3) $(_erm_new_overrides)) \ $(eval _erm_new_modules := $(filter-out $(_erm_all_overrides), $(_erm_new_modules))) \ $(eval $(1) := $(filter-out $(_erm_new_overrides),$($(1)))) \ $(eval $(1) += $(_erm_new_modules)) \ $(if $(_erm_new_modules),\ $(call expand-required-modules,$(1),$(_erm_new_modules),$(_erm_all_overrides))) endef # Same as expand-required-modules above, but does not handle module overrides, as # we don't intend to support them on the host. # $(1): The variable name that holds the initial module name list. # the variable will be modified to hold the expanded results. # $(2): The initial module name list. # $(3): HOST or HOST_CROSS depending on whether we're expanding host or host cross modules # Returns empty string (maybe with some whitespaces). define expand-required-host-modules $(eval _erm_req := $(foreach m,$(2),$(ALL_MODULES.$(m).REQUIRED_FROM_$(3)))) \ $(eval _erm_new_modules := $(sort $(filter-out $($(1)),$(_erm_req)))) \ $(eval $(1) += $(_erm_new_modules)) \ $(if $(_erm_new_modules),\ $(call expand-required-host-modules,$(1),$(_erm_new_modules),$(3))) endef # Transforms paths relative to PRODUCT_OUT to absolute paths. # $(1): list of relative paths # $(2): optional suffix to append to paths define resolve-product-relative-paths $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),\ $(subst $(_product_path_placeholder),$(TARGET_COPY_OUT_PRODUCT),\ $(subst $(_system_ext_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_EXT),\ $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),\ $(subst $(_vendor_dlkm_path_placeholder),$(TARGET_COPY_OUT_VENDOR_DLKM),\ $(subst $(_odm_dlkm_path_placeholder),$(TARGET_COPY_OUT_ODM_DLKM),\ $(subst $(_system_dlkm_path_placeholder),$(TARGET_COPY_OUT_SYSTEM_DLKM),\ $(foreach p,$(1),$(call append-path,$(PRODUCT_OUT),$(p)$(2)))))))))) endef # Returns modules included automatically as a result of certain BoardConfig # variables being set. define auto-included-modules $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver)) \ llndk.libraries.txt \ $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \ $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \ $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \ $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \ endef # Lists the modules particular product installs. # The base list of modules to build for this product is specified # by the appropriate product definition file, which was included # by product_config.mk. # Name resolution for PRODUCT_PACKAGES: # foo:32 resolves to foo_32; # foo:64 resolves to foo; # foo resolves to both foo and foo_32 (if foo_32 is defined). # # Name resolution for LOCAL_REQUIRED_MODULES: # See the select-bitness-of-required-modules definition. # $(1): product makefile define product-installed-modules $(eval _pif_modules := \ $(call get-product-var,$(1),PRODUCT_PACKAGES) \ $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \ $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \ $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \ $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \ $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \ $(if $(filter arm64,$(TARGET_ARCH) $(TARGET_2ND_ARCH)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ARM64)) \ $(if $(PRODUCT_SHIPPING_API_LEVEL), \ $(if $(call math_gt_or_eq,29,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29)) \ $(if $(call math_gt_or_eq,33,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33)) \ $(if $(call math_gt_or_eq,34,$(PRODUCT_SHIPPING_API_LEVEL)),$(call get-product-var,$(1),PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)) \ ) \ $(call auto-included-modules) \ ) \ $(eval ### Filter out the overridden packages and executables before doing expansion) \ $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \ $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \ $(eval ### Resolve the :32 :64 module name) \ $(eval _pif_modules := $(sort $(call resolve-bitness-for-modules,TARGET,$(_pif_modules)))) \ $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \ $(_pif_modules) endef # Lists most of the files a particular product installs. # It gives all the installed files for all modules returned by product-installed-modules, # and also includes PRODUCT_COPY_FILES. define product-installed-files $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(call product-installed-modules,$(1)))) \ $(call resolve-product-relative-paths,\ $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf)))) endef # Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead # This does support the :32 / :64 syntax, but does not support module overrides. define host-installed-files $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \ $(eval ### Split host vs host cross modules) \ $(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \ $(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \ $(eval ### Resolve the :32 :64 module name) \ $(eval _hif_modules := $(sort $(call resolve-bitness-for-modules,HOST,$(_hif_modules)))) \ $(eval _hcif_modules := $(sort $(call resolve-bitness-for-modules,HOST_CROSS,$(_hcif_modules)))) \ $(call expand-required-host-modules,_hif_modules,$(_hif_modules),HOST) \ $(call expand-required-host-modules,_hcif_modules,$(_hcif_modules),HOST_CROSS) \ $(filter $(HOST_OUT)/%,$(call module-installed-files, $(_hif_modules))) \ $(filter $(HOST_CROSS_OUT)/%,$(call module-installed-files, $(_hcif_modules))) endef # Fails the build if the given list is non-empty, and prints it entries (stripping PRODUCT_OUT). # $(1): list of files to print # $(2): heading to print on failure define maybe-print-list-and-error $(if $(strip $(1)), \ $(warning $(2)) \ $(info Offending entries:) \ $(foreach e,$(sort $(1)),$(info $(patsubst $(PRODUCT_OUT)/%,%,$(e)))) \ $(error Build failed) \ ) endef ifeq ($(HOST_OS),darwin) # Target builds are not supported on Mac product_target_FILES := product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT)) else ifdef FULL_BUILD ifneq (true,$(ALLOW_MISSING_DEPENDENCIES)) # Check to ensure that all modules in PRODUCT_PACKAGES exist (opt in per product) ifeq (true,$(PRODUCT_ENFORCE_PACKAGES_EXIST)) _allow_list := $(PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST) _modules := $(PRODUCT_PACKAGES) # Strip :32 and :64 suffixes _modules := $(patsubst %:32,%,$(_modules)) _modules := $(patsubst %:64,%,$(_modules)) # Quickly check all modules in PRODUCT_PACKAGES exist. We check for the # existence if either or the _32 variant. _nonexistent_modules := $(foreach m,$(_modules), \ $(if $(or $(ALL_MODULES.$(m).PATH),$(call get-modules-for-2nd-arch,TARGET,$(m))),,$(m))) $(call maybe-print-list-and-error,$(filter-out $(_allow_list),$(_nonexistent_modules)),\ $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_PACKAGES) # TODO(b/182105280): Consider re-enabling this check when the ART modules # have been cleaned up from the allowed_list in target/product/generic.mk. #$(call maybe-print-list-and-error,$(filter-out $(_nonexistent_modules),$(_allow_list)),\ # $(INTERNAL_PRODUCT) includes redundant allow list entries for non-existent PRODUCT_PACKAGES) endif # Check to ensure that all modules in PRODUCT_HOST_PACKAGES exist # # Many host modules are Linux-only, so skip this check on Mac. If we ever have Mac-only modules, # maybe it would make sense to have PRODUCT_HOST_PACKAGES_LINUX/_DARWIN? ifneq ($(HOST_OS),darwin) _modules := $(PRODUCT_HOST_PACKAGES) # Strip :32 and :64 suffixes _modules := $(patsubst %:32,%,$(_modules)) _modules := $(patsubst %:64,%,$(_modules)) _nonexistent_modules := $(foreach m,$(_modules),\ $(if $(ALL_MODULES.$(m).REQUIRED_FROM_HOST)$(filter $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,$(m))) $(call maybe-print-list-and-error,$(_nonexistent_modules),\ $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_HOST_PACKAGES) endif endif # Modules may produce only host installed files in unbundled builds. ifeq (,$(TARGET_BUILD_UNBUNDLED)) _modules := $(call resolve-bitness-for-modules,TARGET, \ $(PRODUCT_PACKAGES) \ $(PRODUCT_PACKAGES_DEBUG) \ $(PRODUCT_PACKAGES_DEBUG_ASAN) \ $(PRODUCT_PACKAGES_ENG) \ $(PRODUCT_PACKAGES_TESTS)) _host_modules := $(foreach m,$(_modules), \ $(if $(ALL_MODULES.$(m).INSTALLED),\ $(if $(filter-out $(HOST_OUT_ROOT)/%,$(ALL_MODULES.$(m).INSTALLED)),,\ $(m)))) ifeq ($(TARGET_ARCH),riscv64) # HACK: riscv64 can't build the device version of bcc and ld.mc due to a # dependency on an old version of LLVM, but they are listed in # base_system.mk which can't add them conditionally based on the target # architecture. _host_modules := $(filter-out bcc ld.mc,$(_host_modules)) endif $(call maybe-print-list-and-error,$(sort $(_host_modules)),\ Host modules should be in PRODUCT_HOST_PACKAGES$(comma) not PRODUCT_PACKAGES) endif product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT)) product_target_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT)) # WARNING: The product_MODULES variable is depended on by external files. # It contains the list of register names that will be installed on the device product_MODULES := $(_pif_modules) # Verify the artifact path requirements made by included products. is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true) ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS))) include $(BUILD_SYSTEM)/artifact_path_requirements.mk endif else # We're not doing a full build, and are probably only including # a subset of the module makefiles. Don't try to build any modules # requested by the product, because we probably won't have rules # to build them. product_target_FILES := product_host_FILES := endif # TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES # and get rid of it from this list. modules_to_install := $(sort \ $(ALL_DEFAULT_INSTALLED_MODULES) \ $(product_target_FILES) \ $(product_host_FILES) \ $(CUSTOM_MODULES) \ ) # Deduplicate compatibility suite dist files across modules and packages before # copying them to their requested locations. Assign the eval result to an unused # var to prevent Make from trying to make a sense of it. _unused := $(call copy-many-files, $(sort $(ALL_COMPATIBILITY_DIST_FILES))) ifdef is_sdk_build # Ensure every module listed in PRODUCT_PACKAGES* gets something installed # TODO: Should we do this for all builds and not just the sdk? dangling_modules := $(foreach m, $(PRODUCT_PACKAGES), \ $(if $(strip $(ALL_MODULES.$(m).INSTALLED) $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).INSTALLED)),,\ $(eval dangling_modules += $(m)))) ifneq ($(dangling_modules),) $(warning: Modules '$(dangling_modules)' in PRODUCT_PACKAGES have nothing to install!) endif $(foreach m, $(PRODUCT_PACKAGES_DEBUG), \ $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_DEBUG has nothing to install!))) $(foreach m, $(PRODUCT_PACKAGES_ENG), \ $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_ENG has nothing to install!))) $(foreach m, $(PRODUCT_PACKAGES_TESTS), \ $(if $(strip $(ALL_MODULES.$(m).INSTALLED)),,\ $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_TESTS has nothing to install!))) endif ifneq ($(TARGET_BUILD_APPS),) # If this build is just for apps, only build apps and not the full system by default. ifneq ($(filter all,$(TARGET_BUILD_APPS)),) # The magic goal "all" used to build all apps in the source tree. This was deprecated # so that we can know all TARGET_BUILD_APPS apps are built with soong for soong-only builds. $(error TARGET_BUILD_APPS=all is deprecated) else unbundled_build_modules := $(sort $(TARGET_BUILD_APPS)) endif endif # build/make/core/Makefile contains extra stuff that we don't want to pollute this # top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES # contains everything that's built during the current make, but it also further # extends ALL_DEFAULT_INSTALLED_MODULES. ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install) ifeq ($(HOST_OS),linux) include $(BUILD_SYSTEM)/Makefile endif modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES)) ALL_DEFAULT_INSTALLED_MODULES := ifdef FULL_BUILD # # Used by the cleanup logic in soong_ui to remove files that should no longer # be installed. # # Include all tests, so that we remove them from the test suites / testcase # folders when they are removed. test_files := $(foreach ts,$(ALL_COMPATIBILITY_SUITES),$(COMPATIBILITY.$(ts).FILES)) $(shell mkdir -p $(PRODUCT_OUT) $(HOST_OUT)) $(file >$(PRODUCT_OUT)/.installable_files$(if $(filter address,$(SANITIZE_TARGET)),_asan), \ $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%, \ $(modules_to_install) $(test_files))))) $(file >$(HOST_OUT)/.installable_test_files,$(sort \ $(patsubst $(HOST_OUT)/%,%,$(filter $(HOST_OUT)/%, \ $(test_files))))) test_files := endif # Some notice deps refer to module names without prefix or arch suffix where # only the variants with them get built. # fix-notice-deps replaces those unadorned module names with every built variant. $(call fix-notice-deps) # These are additional goals that we build, in order to make sure that there # is as little code as possible in the tree that doesn't build. modules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED)) # If you would like to build all goals, and not skip any intermediate # steps, you can pass the "all" modifier goal on the commandline. ifneq ($(filter all,$(MAKECMDGOALS)),) modules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT)) endif # Build docs as part of checkbuild to catch more breakages. modules_to_check += $(ALL_DOCS) # for easier debugging modules_to_check := $(sort $(modules_to_check)) #$(error modules_to_check $(modules_to_check)) # ------------------------------------------------------------------- # This is used to to get the ordering right, you can also use these, # but they're considered undocumented, so don't complain if their # behavior changes. # An internal target that depends on all copied headers # (see copy_headers.make). Other targets that need the # headers to be copied first can depend on this target. .PHONY: all_copied_headers all_copied_headers: ; $(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers # All the droid stuff, in directories .PHONY: files files: $(modules_to_install) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) # ------------------------------------------------------------------- .PHONY: checkbuild checkbuild: $(modules_to_check) droid_targets check-elf-files ifeq (true,$(ANDROID_BUILD_EVERYTHING_BY_DEFAULT)) droid: checkbuild endif .PHONY: ramdisk ramdisk: $(INSTALLED_RAMDISK_TARGET) .PHONY: ramdisk_debug ramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET) .PHONY: ramdisk_test_harness ramdisk_test_harness: $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) .PHONY: userdataimage userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET) ifneq (,$(filter userdataimage, $(MAKECMDGOALS))) $(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET)) endif .PHONY: cacheimage cacheimage: $(INSTALLED_CACHEIMAGE_TARGET) .PHONY: bptimage bptimage: $(INSTALLED_BPTIMAGE_TARGET) .PHONY: vendorimage vendorimage: $(INSTALLED_VENDORIMAGE_TARGET) .PHONY: vendorbootimage vendorbootimage: $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) .PHONY: vendorkernelbootimage vendorkernelbootimage: $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) .PHONY: vendorbootimage_debug vendorbootimage_debug: $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) .PHONY: vendorbootimage_test_harness vendorbootimage_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) .PHONY: vendorramdisk vendorramdisk: $(INSTALLED_VENDOR_RAMDISK_TARGET) .PHONY: vendorkernelramdisk vendorkernelramdisk: $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) .PHONY: vendorramdisk_debug vendorramdisk_debug: $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) .PHONY: vendorramdisk_test_harness vendorramdisk_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) .PHONY: productimage productimage: $(INSTALLED_PRODUCTIMAGE_TARGET) .PHONY: systemextimage systemextimage: $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) .PHONY: odmimage odmimage: $(INSTALLED_ODMIMAGE_TARGET) .PHONY: vendor_dlkmimage vendor_dlkmimage: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) .PHONY: odm_dlkmimage odm_dlkmimage: $(INSTALLED_ODM_DLKMIMAGE_TARGET) .PHONY: system_dlkmimage system_dlkmimage: $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) .PHONY: systemotherimage systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) .PHONY: superimage_empty superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) .PHONY: bootimage bootimage: $(INSTALLED_BOOTIMAGE_TARGET) .PHONY: initbootimage initbootimage: $(INSTALLED_INIT_BOOT_IMAGE_TARGET) ifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST)) $(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET)) endif .PHONY: bootimage_debug bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) .PHONY: bootimage_test_harness bootimage_test_harness: $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) .PHONY: vbmetaimage vbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET) .PHONY: vbmetasystemimage vbmetasystemimage: $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) .PHONY: vbmetavendorimage vbmetavendorimage: $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) .PHONY: vbmetacustomimages vbmetacustomimages: $(foreach partition,$(call to-upper,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)),$(INSTALLED_VBMETA_$(partition)IMAGE_TARGET)) # The droidcore-unbundled target depends on the subset of targets necessary to # perform a full system build (either unbundled or not). .PHONY: droidcore-unbundled droidcore-unbundled: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \ $(INSTALLED_FILES_OUTSIDE_IMAGES) \ $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \ $(INSTALLED_DTBOIMAGE_TARGET) \ $(INSTALLED_RADIOIMAGE_TARGET) \ $(INSTALLED_DEBUG_RAMDISK_TARGET) \ $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_VBMETAIMAGE_TARGET) \ $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \ $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_BPTIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \ $(INSTALLED_ODM_DLKMIMAGE_TARGET) \ $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \ $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \ $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \ $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \ $(INSTALLED_FILES_FILE) \ $(INSTALLED_FILES_JSON) \ $(INSTALLED_FILES_FILE_VENDOR) \ $(INSTALLED_FILES_JSON_VENDOR) \ $(INSTALLED_FILES_FILE_ODM) \ $(INSTALLED_FILES_JSON_ODM) \ $(INSTALLED_FILES_FILE_VENDOR_DLKM) \ $(INSTALLED_FILES_JSON_VENDOR_DLKM) \ $(INSTALLED_FILES_FILE_ODM_DLKM) \ $(INSTALLED_FILES_JSON_ODM_DLKM) \ $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \ $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \ $(INSTALLED_FILES_FILE_PRODUCT) \ $(INSTALLED_FILES_JSON_PRODUCT) \ $(INSTALLED_FILES_FILE_SYSTEM_EXT) \ $(INSTALLED_FILES_JSON_SYSTEM_EXT) \ $(INSTALLED_FILES_FILE_SYSTEMOTHER) \ $(INSTALLED_FILES_JSON_SYSTEMOTHER) \ $(INSTALLED_FILES_FILE_RAMDISK) \ $(INSTALLED_FILES_JSON_RAMDISK) \ $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \ $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \ $(INSTALLED_FILES_FILE_ROOT) \ $(INSTALLED_FILES_JSON_ROOT) \ $(INSTALLED_FILES_FILE_RECOVERY) \ $(INSTALLED_FILES_JSON_RECOVERY) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) # The droidcore target depends on the droidcore-unbundled subset and any other # targets for a non-unbundled (full source) full system build. .PHONY: droidcore droidcore: droidcore-unbundled # dist_files only for putting your library into the dist directory with a full build. .PHONY: dist_files $(call dist-for-goals, dist_files, $(PRODUCT_OUT)/module-info.json) .PHONY: apps_only ifeq ($(HOST_OS),darwin) # Mac only supports building host modules droid_targets: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) dist_files else ifneq ($(TARGET_BUILD_APPS),) # If this build is just for apps, only build apps and not the full system by default. # Dist the installed files if they exist, except the installed symlinks. dist-for-goals emits # `cp src dest` commands, which will fail to copy dangling symlinks. apps_only_installed_files := $(foreach m,$(unbundled_build_modules),\ $(filter-out $(ALL_MODULES.$(m).INSTALLED_SYMLINKS),$(ALL_MODULES.$(m).INSTALLED))) $(call dist-for-goals,apps_only, $(apps_only_installed_files)) # Dist the bundle files if they exist. apps_only_bundle_files := $(foreach m,$(unbundled_build_modules),\ $(if $(ALL_MODULES.$(m).BUNDLE),$(ALL_MODULES.$(m).BUNDLE):$(m)-base.zip)) $(call dist-for-goals,apps_only, $(apps_only_bundle_files)) # Dist the lint reports if they exist. apps_only_lint_report_files := $(foreach m,$(unbundled_build_modules),\ $(foreach report,$(ALL_MODULES.$(m).LINT_REPORTS),\ $(report):$(m)-$(notdir $(report)))) .PHONY: lint-check lint-check: $(foreach f, $(apps_only_lint_report_files), $(call word-colon,1,$(f))) $(call dist-for-goals,lint-check, $(apps_only_lint_report_files)) # For uninstallable modules such as static Java library, we have to dist the built file, # as . apps_only_dist_built_files := $(foreach m,$(unbundled_build_modules),$(if $(ALL_MODULES.$(m).INSTALLED),,\ $(if $(ALL_MODULES.$(m).BUILT),$(ALL_MODULES.$(m).BUILT):$(m)$(suffix $(ALL_MODULES.$(m).BUILT)))\ $(if $(ALL_MODULES.$(m).AAR),$(ALL_MODULES.$(m).AAR):$(m).aar)\ )) $(call dist-for-goals,apps_only, $(apps_only_dist_built_files)) ifeq ($(EMMA_INSTRUMENT),true) $(JACOCO_REPORT_CLASSES_ALL) : $(apps_only_installed_files) $(call dist-for-goals,apps_only, $(JACOCO_REPORT_CLASSES_ALL)) endif $(PROGUARD_DICT_ZIP) : $(apps_only_installed_files) $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_MAPPING)) $(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) $(PROGUARD_USAGE_ZIP) : $(apps_only_installed_files) $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_USAGE_ZIP)) $(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) $(SYMBOLS_ZIP) : $(apps_only_installed_files) $(call dist-for-goals-with-filenametag,apps_only, $(SYMBOLS_ZIP) $(SYMBOLS_MAPPING)) $(call declare-container-license-deps,$(SYMBOLS_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) $(COVERAGE_ZIP) : $(apps_only_installed_files) $(call dist-for-goals,apps_only, $(COVERAGE_ZIP)) $(call declare-container-license-deps,$(COVERAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/) apps_only: $(unbundled_build_modules) droid_targets: apps_only # NOTICE files for a apps_only build $(eval $(call html-notice-rule,$(target_notice_file_html_or_xml),"Apps","Notices for files for apps:",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/)) $(eval $(call text-notice-rule,$(target_notice_file_txt),"Apps","Notices for files for apps:",$(unbundled_build_modules),$(PRODUCT_OUT)/ $(HOST_OUT)/)) $(call declare-0p-target,$(target_notice_file_txt)) $(call declare-0p-target,$(target_notice_html_or_xml)) else ifeq ($(TARGET_BUILD_UNBUNDLED),$(TARGET_BUILD_UNBUNDLED_IMAGE)) # Truth table for entering this block of code: # TARGET_BUILD_UNBUNDLED | TARGET_BUILD_UNBUNDLED_IMAGE | Action # -----------------------|------------------------------|------------------------- # not set | not set | droidcore path # not set | true | invalid # true | not set | skip # true | true | droidcore-unbundled path # We dist the following targets only for droidcore full build. These items # can include java-related targets that would cause building framework java # sources in a droidcore full build. $(call dist-for-goals, droidcore, \ $(APPCOMPAT_ZIP) \ ) # We dist the following targets for droidcore-unbundled (and droidcore since # droidcore depends on droidcore-unbundled). The droidcore-unbundled target # is a subset of droidcore. It can be used used for an unbundled build to # avoid disting targets that would cause building framework java sources, # which we want to avoid in an unbundled build. $(call dist-for-goals-with-filenametag, droidcore-unbundled, \ $(INTERNAL_UPDATE_PACKAGE_TARGET) \ $(INTERNAL_OTA_PACKAGE_TARGET) \ $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) \ $(BUILT_RAMDISK_16K_TARGET) \ $(BUILT_KERNEL_16K_TARGET) \ $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) \ $(SYMBOLS_ZIP) \ $(SYMBOLS_MAPPING) \ $(PROGUARD_DICT_ZIP) \ $(PROGUARD_DICT_MAPPING) \ $(PROGUARD_USAGE_ZIP) \ $(BUILT_TARGET_FILES_PACKAGE) \ ) $(call dist-for-goals, droidcore-unbundled, \ $(INTERNAL_OTA_METADATA) \ $(COVERAGE_ZIP) \ $(INSTALLED_FILES_FILE) \ $(INSTALLED_FILES_JSON) \ $(INSTALLED_FILES_FILE_VENDOR) \ $(INSTALLED_FILES_JSON_VENDOR) \ $(INSTALLED_FILES_FILE_ODM) \ $(INSTALLED_FILES_JSON_ODM) \ $(INSTALLED_FILES_FILE_VENDOR_DLKM) \ $(INSTALLED_FILES_JSON_VENDOR_DLKM) \ $(INSTALLED_FILES_FILE_ODM_DLKM) \ $(INSTALLED_FILES_JSON_ODM_DLKM) \ $(INSTALLED_FILES_FILE_SYSTEM_DLKM) \ $(INSTALLED_FILES_JSON_SYSTEM_DLKM) \ $(INSTALLED_FILES_FILE_PRODUCT) \ $(INSTALLED_FILES_JSON_PRODUCT) \ $(INSTALLED_FILES_FILE_SYSTEM_EXT) \ $(INSTALLED_FILES_JSON_SYSTEM_EXT) \ $(INSTALLED_FILES_FILE_SYSTEMOTHER) \ $(INSTALLED_FILES_JSON_SYSTEMOTHER) \ $(INSTALLED_FILES_FILE_RECOVERY) \ $(INSTALLED_FILES_JSON_RECOVERY) \ $(if $(BUILDING_VENDOR_IMAGE), $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ $(INSTALLED_MISC_INFO_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(DEXPREOPT_CONFIG_ZIP) \ ) # Put a copy of the radio/bootloader files in the dist dir. $(foreach f,$(INSTALLED_RADIOIMAGE_TARGET), \ $(call dist-for-goals, droidcore-unbundled, $(f))) ifneq ($(ANDROID_BUILD_EMBEDDED),true) $(call dist-for-goals-with-filenametag, droidcore, \ $(INTERNAL_EMULATOR_PACKAGE_TARGET) \ ) endif $(call dist-for-goals, droidcore-unbundled, \ $(INSTALLED_FILES_FILE_ROOT) \ $(INSTALLED_FILES_JSON_ROOT) \ ) $(call dist-for-goals, droidcore-unbundled, \ $(INSTALLED_FILES_FILE_RAMDISK) \ $(INSTALLED_FILES_JSON_RAMDISK) \ $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \ $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_KERNEL_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_KERNEL_RAMDISK) \ $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) \ $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK) \ $(INSTALLED_DEBUG_RAMDISK_TARGET) \ $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \ $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \ $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \ $(INSTALLED_VENDOR_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \ $(INSTALLED_VENDOR_KERNEL_RAMDISK_TARGET) \ ) ifeq ($(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST),true) $(call dist-for-goals, droidcore-unbundled, $(INSTALLED_BOOTIMAGE_TARGET)) endif ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) $(call dist-for-goals, droidcore-unbundled, \ $(recovery_ramdisk) \ ) endif ifeq ($(EMMA_INSTRUMENT),true) $(call dist-for-goals, dist_files, $(JACOCO_REPORT_CLASSES_ALL)) endif ifdef CLANG_COVERAGE $(foreach f,$(SOONG_NDK_API_XML), \ $(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f)))) $(foreach f,$(SOONG_CC_API_XML), \ $(call dist-for-goals,droidcore,$(f):cc_apis/$(notdir $(f)))) endif # For full system build (whether unbundled or not), we configure # droid_targets to depend on droidcore-unbundled, which will set up the full # system dependencies and also dist the subset of targets that correspond to # an unbundled build (exclude building some framework sources). droid_targets: droidcore-unbundled ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE)) # If we're building a full system (including the framework sources excluded # by droidcore-unbundled), we configure droid_targets also to depend on # droidcore, which includes all dist for droidcore, and will build the # necessary framework sources. droid_targets: droidcore dist_files endif endif # TARGET_BUILD_UNBUNDLED == TARGET_BUILD_UNBUNDLED_IMAGE .PHONY: docs docs: $(ALL_DOCS) .PHONY: sdk sdk_addon ifeq ($(HOST_OS),linux) ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET) sdk: $(ALL_SDK_TARGETS) $(call dist-for-goals-with-filenametag,sdk,$(ALL_SDK_TARGETS)) endif # umbrella targets to assit engineers in verifying builds .PHONY: java native target host java-host java-target native-host native-target \ java-host-tests java-target-tests native-host-tests native-target-tests \ java-tests native-tests host-tests target-tests tests java-dex \ native-host-cross # some synonyms .PHONY: host-java target-java host-native target-native \ target-java-tests target-native-tests host-java : java-host target-java : java-target host-native : native-host target-native : native-target target-java-tests : java-target-tests target-native-tests : native-target-tests tests : host-tests target-tests # Phony target to run all java compilations that use javac .PHONY: javac-check .PHONY: findbugs findbugs: $(INTERNAL_FINDBUGS_HTML_TARGET) $(INTERNAL_FINDBUGS_XML_TARGET) .PHONY: check-elf-files check-elf-files: .PHONY: dump-files dump-files: @echo "Target files for $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) ($(INTERNAL_PRODUCT)):" @echo $(sort $(patsubst $(PRODUCT_OUT)/%,%,$(filter $(PRODUCT_OUT)/%,$(modules_to_install)))) | tr -s ' ' '\n' @echo Successfully dumped product target file list. .PHONY: tidy_only tidy_only: @echo Successfully make tidy_only. ndk: $(SOONG_OUT_DIR)/ndk.timestamp .PHONY: ndk # Checks that allowed_deps.txt remains up to date ifneq ($(UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK),true) droidcore: ${APEX_ALLOWED_DEPS_CHECK} endif # Create a license metadata rule per module. Could happen in base_rules.mk or # notice_files.mk; except, it has to happen after fix-notice-deps to avoid # missing dependency errors. $(call build-license-metadata) # Generate SBOM in SPDX format product_copy_files_without_owner := $(foreach pcf,$(PRODUCT_COPY_FILES),$(call word-colon,1,$(pcf)):$(call word-colon,2,$(pcf))) ifeq ($(TARGET_BUILD_APPS),) dest_files_without_source := $(sort $(foreach pcf,$(product_copy_files_without_owner),$(if $(wildcard $(call word-colon,1,$(pcf))),,$(call word-colon,2,$(pcf))))) dest_files_without_source := $(addprefix $(PRODUCT_OUT)/,$(dest_files_without_source)) filter_out_files := \ $(PRODUCT_OUT)/apex/% \ $(PRODUCT_OUT)/fake_packages/% \ $(PRODUCT_OUT)/testcases/% \ $(dest_files_without_source) \ $(PRODUCT_OUT)/required_images # Check if each partition image is built, if not filter out all its installed files # Also check if a partition uses prebuilt image file, save the info if prebuilt image is used. PREBUILT_PARTITION_COPY_FILES := # product.img ifndef BUILDING_PRODUCT_IMAGE filter_out_files += $(PRODUCT_OUT)/product/% ifdef BOARD_PREBUILT_PRODUCTIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_PRODUCTIMAGE):$(INSTALLED_PRODUCTIMAGE_TARGET) endif endif # system.img ifndef BUILDING_SYSTEM_IMAGE filter_out_files += $(PRODUCT_OUT)/system/% endif # system_dlkm.img ifndef BUILDING_SYSTEM_DLKM_IMAGE filter_out_files += $(PRODUCT_OUT)/system_dlkm/% ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_SYSTEM_DLKMIMAGE):$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) endif endif # system_ext.img ifndef BUILDING_SYSTEM_EXT_IMAGE filter_out_files += $(PRODUCT_OUT)/system_ext/% ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_SYSTEM_EXTIMAGE):$(INSTALLED_SYSTEM_EXTIMAGE_TARGET) endif endif # system_other.img ifndef BUILDING_SYSTEM_OTHER_IMAGE filter_out_files += $(PRODUCT_OUT)/system_other/% endif # odm.img ifndef BUILDING_ODM_IMAGE filter_out_files += $(PRODUCT_OUT)/odm/% ifdef BOARD_PREBUILT_ODMIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_ODMIMAGE):$(INSTALLED_ODMIMAGE_TARGET) endif endif # odm_dlkm.img ifndef BUILDING_ODM_DLKM_IMAGE filter_out_files += $(PRODUCT_OUT)/odm_dlkm/% ifdef BOARD_PREBUILT_ODM_DLKMIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_ODM_DLKMIMAGE):$(INSTALLED_ODM_DLKMIMAGE_TARGET) endif endif # vendor.img ifndef BUILDING_VENDOR_IMAGE filter_out_files += $(PRODUCT_OUT)/vendor/% ifdef BOARD_PREBUILT_VENDORIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_VENDORIMAGE):$(INSTALLED_VENDORIMAGE_TARGET) endif endif # vendor_dlkm.img ifndef BUILDING_VENDOR_DLKM_IMAGE filter_out_files += $(PRODUCT_OUT)/vendor_dlkm/% ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_VENDOR_DLKMIMAGE):$(INSTALLED_VENDOR_DLKMIMAGE_TARGET) endif endif # cache.img ifndef BUILDING_CACHE_IMAGE filter_out_files += $(PRODUCT_OUT)/cache/% endif # boot.img ifndef BUILDING_BOOT_IMAGE ifdef BOARD_PREBUILT_BOOTIMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_BOOTIMAGE):$(INSTALLED_BOOTIMAGE_TARGET) endif endif # init_boot.img ifndef BUILDING_INIT_BOOT_IMAGE ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE PREBUILT_PARTITION_COPY_FILES += $(BOARD_PREBUILT_INIT_BOOT_IMAGE):$(INSTALLED_INIT_BOOT_IMAGE_TARGET) endif endif # ramdisk.img ifndef BUILDING_RAMDISK_IMAGE filter_out_files += $(PRODUCT_OUT)/ramdisk/% endif # recovery.img ifndef INSTALLED_RECOVERYIMAGE_TARGET filter_out_files += $(PRODUCT_OUT)/recovery/% endif # userdata.img ifndef BUILDING_USERDATA_IMAGE filter_out_files += $(PRODUCT_OUT)/data/% endif installed_files := $(sort $(filter-out $(filter_out_files),$(filter $(PRODUCT_OUT)/%,$(modules_to_install)))) else installed_files := $(apps_only_installed_files) endif # TARGET_BUILD_APPS metadata_list := $(OUT_DIR)/.module_paths/METADATA.list metadata_files := $(subst $(newline),$(space),$(file <$(metadata_list))) # Create metadata for compliance support in Soong .PHONY: make-compliance-metadata make-compliance-metadata: \ $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv \ $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv: rm -f $@ echo 'installed_file,module_path,is_soong_module,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,static_libs,whole_static_libs,license_text' >> $@ $(foreach f,$(installed_files),\ $(eval _module_name := $(ALL_INSTALLED_FILES.$f)) \ $(eval _path_on_device := $(patsubst $(PRODUCT_OUT)/%,%,$f)) \ $(eval _build_output_path := $(PRODUCT_OUT)/$(_path_on_device)) \ $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \ $(eval _is_soong_module := $(ALL_MODULES.$(_module_name).IS_SOONG_MODULE)) \ $(eval _is_prebuilt_make_module := $(ALL_MODULES.$(_module_name).IS_PREBUILT_MAKE_MODULE)) \ $(eval _product_copy_files := $(sort $(filter %:$(_path_on_device),$(product_copy_files_without_owner)))) \ $(eval _kernel_module_copy_files := $(sort $(filter %$(_path_on_device),$(KERNEL_MODULE_COPY_FILES)))) \ $(eval _is_build_prop := $(call is-build-prop,$f)) \ $(eval _is_notice_file := $(call is-notice-file,$f)) \ $(eval _is_product_system_other_avbkey := $(if $(findstring $f,$(INSTALLED_PRODUCT_SYSTEM_OTHER_AVBKEY_TARGET)),Y)) \ $(eval _is_event_log_tags_file := $(if $(findstring $f,$(event_log_tags_file)),Y)) \ $(eval _is_system_other_odex_marker := $(if $(findstring $f,$(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)),Y)) \ $(eval _is_kernel_modules_blocklist := $(if $(findstring $f,$(ALL_KERNEL_MODULES_BLOCKLIST)),Y)) \ $(eval _is_fsverity_build_manifest_apk := $(if $(findstring $f,$(ALL_FSVERITY_BUILD_MANIFEST_APK)),Y)) \ $(eval _is_linker_config := $(if $(findstring $f,$(SYSTEM_LINKER_CONFIG) $(vendor_linker_config_file) $(product_linker_config_file)),Y)) \ $(eval _is_partition_compat_symlink := $(if $(findstring $f,$(PARTITION_COMPAT_SYMLINKS)),Y)) \ $(eval _is_flags_file := $(if $(findstring $f, $(ALL_FLAGS_FILES)),Y)) \ $(eval _is_rootdir_symlink := $(if $(findstring $f, $(ALL_ROOTDIR_SYMLINKS)),Y)) \ $(eval _is_platform_generated := $(if $(_is_soong_module),,$(_is_build_prop)$(_is_notice_file)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)$(_is_flags_file)$(_is_rootdir_symlink))) \ $(eval _static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.STATIC_LIBRARIES))) \ $(eval _whole_static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.WHOLE_STATIC_LIBRARIES))) \ $(eval _license_text := $(if $(filter $(_build_output_path),$(ALL_NON_MODULES)),$(ALL_NON_MODULES.$(_build_output_path).NOTICES),\ $(if $(_is_partition_compat_symlink),build/soong/licenses/LICENSE))) \ echo '$(_build_output_path),$(_module_path),$(_is_soong_module),$(_is_prebuilt_make_module),$(_product_copy_files),$(_kernel_module_copy_files),$(_is_platform_generated),$(_static_libs),$(_whole_static_libs),$(_license_text)' >> $@; \ ) $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv: rm -f $@ echo 'name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files' >> $@ $(foreach m,$(ALL_MODULES), \ $(eval _module_name := $m) \ $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \ $(eval _make_module_class := $(ALL_MODULES.$(_module_name).CLASS)) \ $(eval _make_module_type := $(ALL_MODULES.$(_module_name).MAKE_MODULE_TYPE)) \ $(eval _static_libs := $(strip $(sort $(ALL_MODULES.$(_module_name).STATIC_LIBS)))) \ $(eval _whole_static_libs := $(strip $(sort $(ALL_MODULES.$(_module_name).WHOLE_STATIC_LIBS)))) \ $(eval _built_files := $(strip $(sort $(ALL_MODULES.$(_module_name).BUILT)))) \ $(eval _installed_files := $(strip $(sort $(ALL_MODULES.$(_module_name).INSTALLED)))) \ $(eval _is_soong_module := $(ALL_MODULES.$(_module_name).IS_SOONG_MODULE)) \ $(if $(_is_soong_module),, \ echo '$(_module_name),$(_module_path),$(_make_module_class),$(_make_module_type),$(_static_libs),$(_whole_static_libs),$(_built_files),$(_installed_files)' >> $@; \ ) \ ) $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/installed_files.stamp: $(installed_files) touch $@ # Remove the always_dirty_file.txt whenever the makefile is evaluated $(shell rm -f $(PRODUCT_OUT)/always_dirty_file.txt) $(PRODUCT_OUT)/always_dirty_file.txt: touch $@ .PHONY: sbom ifneq ($(TARGET_BUILD_APPS),) # Create build rules for generating SBOMs of unbundled APKs and APEXs # $1: sbom file # $2: sbom fragment file # $3: installed file # $4: sbom-metadata.csv file define generate-app-sbom $(eval _path_on_device := $(patsubst $(PRODUCT_OUT)/%,%,$(3))) $(eval _module_name := $(ALL_INSTALLED_FILES.$(3))) $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) $(eval _dep_modules := $(filter %.$(_module_name),$(ALL_MODULES)) $(filter %.$(_module_name)$(TARGET_2ND_ARCH_MODULE_SUFFIX),$(ALL_MODULES))) $(eval _is_apex := $(filter %.apex,$(3))) $(4): rm -rf $$@ echo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $$@ echo /$(_path_on_device),$(_module_path),$(_soong_module_type),,,,,$(3),,, >> $$@ $(if $(filter %.apex,$(3)),\ $(foreach m,$(_dep_modules),\ echo $(patsubst $(PRODUCT_OUT)/apex/$(_module_name)/%,%,$(ALL_MODULES.$m.INSTALLED)),$(sort $(ALL_MODULES.$m.PATH)),$(sort $(ALL_MODULES.$m.SOONG_MODULE_TYPE)),,,,,$(strip $(ALL_MODULES.$m.BUILT)),,, >> $$@;)) $(2): $(1) $(1): $(4) $(3) $(GEN_SBOM) $(installed_files) $(metadata_list) $(metadata_files) rm -rf $$@ $(GEN_SBOM) --output_file $$@ --metadata $(4) --build_version $$(BUILD_FINGERPRINT_FROM_FILE) --product_mfr "$(PRODUCT_MANUFACTURER)" --json $(if $(filter %.apk,$(3)),--unbundled_apk,--unbundled_apex) endef apps_only_sbom_files := apps_only_fragment_files := $(foreach f,$(filter %.apk %.apex,$(installed_files)), \ $(eval _metadata_csv_file := $(patsubst %,%-sbom-metadata.csv,$f)) \ $(eval _sbom_file := $(patsubst %,%.spdx.json,$f)) \ $(eval _fragment_file := $(patsubst %,%-fragment.spdx,$f)) \ $(eval apps_only_sbom_files += $(_sbom_file)) \ $(eval apps_only_fragment_files += $(_fragment_file)) \ $(eval $(call generate-app-sbom,$(_sbom_file),$(_fragment_file),$f,$(_metadata_csv_file))) \ ) sbom: $(apps_only_sbom_files) $(foreach f,$(apps_only_fragment_files),$(eval apps_only_fragment_dist_files += :sbom/$(notdir $f))) $(foreach f,$(apps_only_sbom_files),$(eval apps_only_sbom_dist_files += :sbom/$(notdir $f))) $(call dist-for-goals,apps_only,$(join $(apps_only_sbom_files),$(apps_only_sbom_dist_files)) $(join $(apps_only_fragment_files),$(apps_only_fragment_dist_files))) endif $(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk) $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing legacy Make module rules ...) ================================================ FILE: core/misc_prebuilt_internal.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################ # Internal build rules for misc prebuilt modules that don't need additional processing ############################################################ prebuilt_module_classes := SCRIPT ETC DATA RENDERSCRIPT_BITCODE ifeq ($(filter $(prebuilt_module_classes),$(LOCAL_MODULE_CLASS)),) $(call pretty-error,misc_prebuilt_internal.mk is for $(prebuilt_module_classes) modules only) endif include $(BUILD_SYSTEM)/base_rules.mk ifneq ($(filter init%rc,$(notdir $(LOCAL_INSTALLED_MODULE)))$(filter %/etc/init/,$(dir $(LOCAL_INSTALLED_MODULE))),) $(eval $(call copy-init-script-file-checked,$(my_prebuilt_src_file),$(LOCAL_BUILT_MODULE))) else $(LOCAL_BUILT_MODULE) : $(my_prebuilt_src_file) $(transform-prebuilt-to-target) endif built_module := $(LOCAL_BUILT_MODULE) ================================================ FILE: core/module_arch_supported.mk ================================================ ########################################################### ## Determine if a module can be built for an arch ## ## Inputs from module makefile: ## my_prefix TARGET_ or HOST_ ## my_module_multilib ## LOCAL_MODULE_$(my_prefix)ARCH ## LOCAL_MODULE_$(my_prefix)ARCH_WARN ## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH ## LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN ## LOCAL_IS_HOST_MODULE ## LOCAL_MODULE_HOST_OS ## ## Inputs from build system: ## $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT ## LOCAL_2ND_ARCH_VAR_PREFIX ## ## Outputs: ## my_module_arch_supported := (true|false) ########################################################### my_module_arch_supported := true ifeq ($(my_module_multilib),none) my_module_arch_supported := false endif ifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),true|32) my_module_arch_supported := false endif ifeq ($($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT)|$(my_module_multilib),|64) my_module_arch_supported := false endif ifneq ($(LOCAL_2ND_ARCH_VAR_PREFIX),) ifeq ($(my_module_multilib),first) my_module_arch_supported := false endif endif ifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH)) ifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH))) my_module_arch_supported := false endif endif ifneq (,$(LOCAL_MODULE_$(my_prefix)ARCH_WARN)) ifeq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH_WARN))) my_module_arch_supported := false $(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) not supported) endif endif ifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH))) my_module_arch_supported := false endif ifneq (,$(filter $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH),$(LOCAL_MODULE_UNSUPPORTED_$(my_prefix)ARCH_WARN))) my_module_arch_supported := false $(warning $(LOCAL_MODULE): architecture $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) unsupported) endif ifdef LOCAL_IS_HOST_MODULE ifneq (,$(LOCAL_MODULE_HOST_OS)) ifneq (,$(filter windows,$(LOCAL_MODULE_HOST_OS))) $(call pretty-error,Windows is only supported in Android.bp files) endif ifeq (,$(filter $($(my_prefix)OS),$(LOCAL_MODULE_HOST_OS))) my_module_arch_supported := false endif endif endif ================================================ FILE: core/multi_prebuilt.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call record-module-type,MULTI_PREBUILT) ifneq ($(LOCAL_MODULE)$(LOCAL_MODULE_CLASS),) $(error $(LOCAL_PATH): LOCAL_MODULE or LOCAL_MODULE_CLASS not needed by \ BUILD_MULTI_PREBUILT, use BUILD_PREBUILT instead!) endif # Save these before they get cleared by CLEAR_VARS. prebuilt_static_libs := $(filter %.a,$(LOCAL_PREBUILT_LIBS)) prebuilt_shared_libs := $(filter-out %.a,$(LOCAL_PREBUILT_LIBS)) prebuilt_executables := $(LOCAL_PREBUILT_EXECUTABLES) prebuilt_java_libraries := $(LOCAL_PREBUILT_JAVA_LIBRARIES) prebuilt_static_java_libraries := $(LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES) prebuilt_is_host := $(LOCAL_IS_HOST_MODULE) prebuilt_module_tags := $(LOCAL_MODULE_TAGS) prebuilt_strip_module := $(LOCAL_STRIP_MODULE) ifndef multi_prebuilt_once multi_prebuilt_once := true # $(1): file list # $(2): IS_HOST_MODULE # $(3): MODULE_CLASS # $(4): MODULE_TAGS # $(6): UNINSTALLABLE_MODULE # $(7): BUILT_MODULE_STEM # $(8): LOCAL_STRIP_MODULE # # Elements in the file list may be bare filenames, # or of the form ":". # If the module name is not specified, the module # name will be the filename with the suffix removed. # define auto-prebuilt-boilerplate $(if $(filter %: :%,$(1)), \ $(error $(LOCAL_PATH): Leading or trailing colons in "$(1)")) \ $(foreach t,$(1), \ $(eval include $(CLEAR_VARS)) \ $(eval LOCAL_IS_HOST_MODULE := $(2)) \ $(eval LOCAL_MODULE_CLASS := $(3)) \ $(eval LOCAL_MODULE_TAGS := $(4)) \ $(eval LOCAL_UNINSTALLABLE_MODULE := $(6)) \ $(eval tw := $(subst :, ,$(strip $(t)))) \ $(if $(word 3,$(tw)),$(error $(LOCAL_PATH): Bad prebuilt filename '$(t)')) \ $(if $(word 2,$(tw)), \ $(eval LOCAL_MODULE := $(word 1,$(tw))) \ $(eval LOCAL_SRC_FILES := $(word 2,$(tw))) \ , \ $(eval LOCAL_MODULE := $(basename $(notdir $(t)))) \ $(eval LOCAL_SRC_FILES := $(t)) \ ) \ $(if $(7), \ $(eval LOCAL_BUILT_MODULE_STEM := $(7)) \ , \ $(if $(word 2,$(tw)), \ $(eval LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)$(suffix $(LOCAL_SRC_FILES))) \ , \ $(eval LOCAL_BUILT_MODULE_STEM := $(notdir $(LOCAL_SRC_FILES))) \ ) \ ) \ $(eval LOCAL_MODULE_SUFFIX := $(suffix $(LOCAL_SRC_FILES))) \ $(eval LOCAL_STRIP_MODULE := $(8)) \ $(eval include $(BUILD_PREBUILT)) \ ) endef endif # multi_prebuilt_once $(call auto-prebuilt-boilerplate, \ $(prebuilt_static_libs), \ $(prebuilt_is_host), \ STATIC_LIBRARIES, \ $(prebuilt_module_tags), \ , \ true) $(call auto-prebuilt-boilerplate, \ $(prebuilt_shared_libs), \ $(prebuilt_is_host), \ SHARED_LIBRARIES, \ $(prebuilt_module_tags), \ , \ , \ , \ $(prebuilt_strip_module)) $(call auto-prebuilt-boilerplate, \ $(prebuilt_executables), \ $(prebuilt_is_host), \ EXECUTABLES, \ $(prebuilt_module_tags)) $(call auto-prebuilt-boilerplate, \ $(prebuilt_java_libraries), \ $(prebuilt_is_host), \ JAVA_LIBRARIES, \ $(prebuilt_module_tags), \ , \ , \ javalib.jar) $(call auto-prebuilt-boilerplate, \ $(prebuilt_static_java_libraries), \ $(prebuilt_is_host), \ JAVA_LIBRARIES, \ $(prebuilt_module_tags), \ , \ true, \ javalib.jar) prebuilt_static_libs := prebuilt_shared_libs := prebuilt_executables := prebuilt_java_libraries := prebuilt_static_java_libraries := prebuilt_is_host := prebuilt_module_tags := $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=MULTI_PREBUILT)) ================================================ FILE: core/multilib.mk ================================================ # Translate LOCAL_32_BIT_ONLY to LOCAL_MULTILIB, # and check LOCAL_MULTILIB is a valid value. Returns module's multilib # setting in my_module_multilib, or empty if not set. my_module_multilib := $(strip $(LOCAL_MULTILIB)) ifndef my_module_multilib ifeq ($(LOCAL_32_BIT_ONLY),true) my_module_multilib := 32 endif else # my_module_multilib defined ifeq (,$(filter 32 64 first both none,$(my_module_multilib))) $(error $(LOCAL_PATH): Invalid LOCAL_MULTILIB specified for module $(LOCAL_MODULE)) endif endif # my_module_multilib defined ================================================ FILE: core/native_benchmark_test_config_template.xml ================================================ ================================================ FILE: core/native_host_test_config_template.xml ================================================ ================================================ FILE: core/native_test.mk ================================================ ########################################### ## A thin wrapper around BUILD_EXECUTABLE ## Common flags for native tests are added. ########################################### $(call record-module-type,NATIVE_TEST) ifdef LOCAL_MODULE_CLASS ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS) $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must be NATIVE_TESTS with BUILD_HOST_NATIVE_TEST) endif endif LOCAL_MODULE_CLASS := NATIVE_TESTS include $(BUILD_SYSTEM)/target_test_internal.mk ifndef LOCAL_MULTILIB ifndef LOCAL_32_BIT_ONLY LOCAL_MULTILIB := both endif endif include $(BUILD_EXECUTABLE) $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=NATIVE_TEST)) ================================================ FILE: core/native_test_config_template.xml ================================================ ================================================ FILE: core/ninja_config.mk ================================================ ifeq ($(filter address,$(SANITIZE_HOST)),) NINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/bin/ninja else NINJA ?= prebuilts/build-tools/$(HOST_PREBUILT_TAG)/asan/bin/ninja endif KATI_OUTPUT_PATTERNS := $(OUT_DIR)/build%.ninja $(OUT_DIR)/ninja%.sh # Modifier goals we don't need to pass to Ninja. NINJA_EXCLUDE_GOALS := all # A list of goals which affect parsing of makefiles and we need to pass to Kati. PARSE_TIME_MAKE_GOALS := \ $(PARSE_TIME_MAKE_GOALS) \ $(dont_bother_goals) \ all \ brillo_tests \ btnod \ build-art% \ build_kernel-nodeps \ clean-oat% \ custom_images \ dicttool_aosp \ docs \ eng \ oem_image \ online-system-api-sdk-docs \ product-graph \ samplecode \ sdk \ sdk_addon \ sdk_repo \ stnod \ test-art% \ user \ userdataimage \ userdebug include $(wildcard vendor/*/build/ninja_config.mk) # Any Android goals that need to be built. ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\ $(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS))) # Temporary compatibility support until the build server configs are updated ANDROID_GOALS := $(patsubst win_sdk,sdk,$(ANDROID_GOALS)) ifneq ($(HOST_OS),linux) ANDROID_GOALS := $(filter-out sdk,$(ANDROID_GOALS)) ANDROID_GOALS := $(patsubst sdk_repo,sdk-repo-build-tools sdk-repo-platform-tools,$(ANDROID_GOALS)) endif # Goals we need to pass to Ninja. NINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS)) ifndef NINJA_GOALS NINJA_GOALS := droid endif # Goals we need to pass to Kati. KATI_GOALS := $(filter $(PARSE_TIME_MAKE_GOALS), $(ANDROID_GOALS)) ================================================ FILE: core/node_fns.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Clears a list of variables using ":=". # # E.g., # $(call clear-var-list,A B C) # would be the same as: # A := # B := # C := # # $(1): list of variable names to clear # define clear-var-list $(foreach v,$(1),$(eval $(v):=)) endef # # Copies a list of variables into another list of variables. # The target list is the same as the source list, but has # a dotted prefix affixed to it. # # E.g., # $(call copy-var-list, PREFIX, A B) # would be the same as: # PREFIX.A := $(A) # PREFIX.B := $(B) # # $(1): destination prefix # $(2): list of variable names to copy # define copy-var-list $(foreach v,$(2),$(eval $(strip $(1)).$(v):=$($(v)))) endef # # Moves a list of variables into another list of variables. # The variable names differ by a prefix. After moving, the # source variable is cleared. # # NOTE: Spaces are not allowed around the prefixes. # # E.g., # $(call move-var-list,SRC,DST,A B) # would be the same as: # DST.A := $(SRC.A) # SRC.A := # DST.B := $(SRC.B) # SRC.B := # # $(1): source prefix # $(2): destination prefix # $(3): list of variable names to move # define move-var-list $(foreach v,$(3), \ $(eval $(2).$(v) := $($(1).$(v))) \ $(eval $(1).$(v) :=) \ ) endef # # $(1): haystack # $(2): needle # # Guarantees that needle appears at most once in haystack, # without changing the order of other elements in haystack. # If needle appears multiple times, only the first occurrance # will survive. # define uniq-word $(strip \ $(if $(filter-out 0 1,$(words $(filter $(2),$(1)))), \ $(eval _uniq_word_seen :=) \ $(foreach w,$(1), \ $(if $(filter $(2),$(w)), \ $(if $(_uniq_word_seen),, \ $(w) \ $(eval _uniq_word_seen := true)), \ $(w))), \ $(1))) endef INHERIT_TAG := @inherit: # # Walks through the list of variables, each qualified by the prefix, # and finds instances of words beginning with INHERIT_TAG. Scrape # off INHERIT_TAG from each matching word, and return the sorted, # unique set of those words. # # E.g., given # PREFIX.A := A $(INHERIT_TAG)aaa B C # PREFIX.B := B $(INHERIT_TAG)aaa C $(INHERIT_TAG)bbb D E # Then # $(call get-inherited-nodes,PREFIX,A B) # returns # aaa bbb # # $(1): variable prefix # $(2): list of variables to check # define get-inherited-nodes $(sort \ $(subst $(INHERIT_TAG),, \ $(filter $(INHERIT_TAG)%, \ $(foreach v,$(2),$($(1).$(v))) \ ))) endef # # for each variable ( (prefix + name) * vars ): # get list of inherited words; if not empty: # for each inherit: # replace the first occurrence with (prefix + inherited + var) # clear the source var so we can't inherit the value twice # # $(1): context prefix # $(2): name of this node # $(3): list of node variable names # $(4): list of single value variable names (subset of $(3)) # define _expand-inherited-values $(foreach v,$(3), \ $(eval ### "Shorthand for the name of the target variable") \ $(eval _eiv_tv := $(1).$(2).$(v)) \ $(eval ### "Get the list of nodes that this variable inherits") \ $(eval _eiv_i := \ $(sort \ $(patsubst $(INHERIT_TAG)%,%, \ $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \ )))) \ $(eval ### "Whether this variable should only take a single value") \ $(eval _eiv_sv := $(filter $(v),$(4))) \ $(foreach i,$(_eiv_i), \ $(eval ### "Make sure that this inherit appears only once") \ $(eval $(_eiv_tv) := \ $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \ $(eval ### "The expanded value, empty if we want a single value and have one") \ $(eval _eiv_ev := \ $(if $(and $(_eiv_sv),$(filter-out $(INHERIT_TAG)%,$($(_eiv_tv)))),,\ $($(1).$(i).$(v)) \ ) \ ) \ $(eval ### "Expand the inherit tag") \ $(eval $(_eiv_tv) := \ $(strip $(patsubst $(INHERIT_TAG)$(i),$(_eiv_ev),$($(_eiv_tv))))) \ $(eval ### "Clear the child so DAGs don't create duplicate entries" ) \ $(eval $(1).$(i).$(v) :=) \ $(eval ### "If we just inherited ourselves, it's a cycle.") \ $(if $(filter $(INHERIT_TAG)$(2),$($(_eiv_tv))), \ $(warning Cycle detected between "$(2)" and "$(i)" for context "$(1)") \ $(error import of "$(2)" failed) \ ) \ ) \ ) \ $(eval _eiv_tv :=) \ $(eval _eiv_i :=) endef # # $(1): context prefix # $(2): makefile representing this node # $(3): list of node variable names # $(4): list of single value variable names (subset of $(3)) # # _include_stack contains the list of included files, with the most recent files first. define _import-node $(eval _include_stack := $(2) $$(_include_stack)) $(call clear-var-list, $(3)) $(eval LOCAL_PATH := $(patsubst %/,%,$(dir $(2)))) $(eval MAKEFILE_LIST :=) $(call dump-import-start,$(_include_stack)) $(call dump-config-vals,$(2),before) $(eval include $(2)) $(call dump-import-done,$(_include_stack)) $(call dump-config-vals,$(2),after) $(eval _included := $(filter-out $(2),$(MAKEFILE_LIST))) $(eval MAKEFILE_LIST :=) $(eval LOCAL_PATH :=) $(call copy-var-list, $(1).$(2), $(3)) $(call clear-var-list, $(3)) $(eval $(1).$(2).inherited := \ $(call get-inherited-nodes,$(1).$(2),$(3))) $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3),$(4)) $(call _expand-inherited-values,$(1),$(2),$(3),$(4)) $(eval $(1).$(2).inherited :=) $(eval _include_stack := $(wordlist 2,9999,$(_include_stack))) endef # # This will generate a warning for _included above # $(if $(_included), \ # $(eval $(warning product spec file: $(2)))\ # $(foreach _inc,$(_included),$(eval $(warning $(space)$(space)$(space)includes: $(_inc)))),) # # # $(1): context prefix # $(2): list of makefiles representing nodes to import # $(3): list of node variable names # $(4): list of single value variable names (subset of $(3)) # #TODO: Make the "does not exist" message more helpful; # should print out the name of the file trying to include it. define _import-nodes-inner $(foreach _in,$(2), \ $(if $(wildcard $(_in)), \ $(if $($(1).$(_in).seen), \ $(eval ### "skipping already-imported $(_in)") \ , \ $(eval $(1).$(_in).seen := true) \ $(call _import-node,$(1),$(strip $(_in)),$(3),$(4)) \ ) \ , \ $(error $(1): "$(_in)" does not exist) \ ) \ ) endef # # $(1): output list variable name, like "PRODUCTS" or "DEVICES" # $(2): list of makefiles representing nodes to import # $(3): list of node variable names # $(4): list with subset of variable names that take only a single value, instead # of the default list semantics # define import-nodes $(call dump-phase-start,$(1),$(2),$(3),$(4),build/make/core/node_fns.mk) \ $(if \ $(foreach _in,$(2), \ $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ $(eval _include_stack := ) \ $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3),$(4)) \ $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \ $(eval _node_import_context :=) \ $(eval $(1) := $($(1)) $(_in)) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ ) \ ,) \ $(call dump-phase-end,build/make/core/node_fns.mk) endef ================================================ FILE: core/notice_files.mk ================================================ ########################################################### ## Track NOTICE files ########################################################### module_license_metadata := $(call local-meta-intermediates-dir)/$(my_register_name).meta_lic $(foreach target,$(ALL_MODULES.$(my_register_name).BUILT) $(ALL_MODULES.$(my_register_name).INSTALLED) $(foreach bi,$(LOCAL_SOONG_BUILT_INSTALLED),$(call word-colon,1,$(bi))),\ $(eval ALL_TARGETS.$(target).META_LIC := $(module_license_metadata))) $(foreach f,$(my_test_data) $(my_test_config),\ $(if $(strip $(ALL_TARGETS.$(call word-colon,1,$(f)).META_LIC)), \ $(call declare-copy-target-license-metadata,$(call word-colon,2,$(f)),$(call word-colon,1,$(f))), \ $(eval ALL_TARGETS.$(call word-colon,2,$(f)).META_LIC := $(module_license_metadata)))) ALL_MODULES.$(my_register_name).META_LIC := $(strip $(ALL_MODULES.$(my_register_name).META_LIC) $(module_license_metadata)) ifdef LOCAL_SOONG_LICENSE_METADATA # Soong modules have already produced a license metadata file, copy it to where Make expects it. $(eval $(call copy-one-license-metadata-file, $(LOCAL_SOONG_LICENSE_METADATA), $(module_license_metadata),$(ALL_MODULES.$(my_register_name).BUILT),$(ALL_MODUES.$(my_register_name).INSTALLED))) else # Make modules don't have enough information to produce a license metadata rule until after fix-notice-deps # has been called, store the necessary information until later. ifneq ($(LOCAL_NOTICE_FILE),) notice_file:=$(strip $(LOCAL_NOTICE_FILE)) else notice_file:=$(strip $(wildcard $(LOCAL_PATH)/LICENSE $(LOCAL_PATH)/LICENCE $(LOCAL_PATH)/NOTICE)) endif ifeq ($(LOCAL_MODULE_CLASS),GYP) # We ignore NOTICE files for modules of type GYP. notice_file := endif ifeq ($(LOCAL_MODULE_CLASS),FAKE) # We ignore NOTICE files for modules of type FAKE. notice_file := endif # Soong generates stub libraries that don't need NOTICE files ifdef LOCAL_NO_NOTICE_FILE ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,LOCAL_NO_NOTICE_FILE should not be used by Android.mk files) endif notice_file := endif ifneq (,$(strip $(LOCAL_LICENSE_PACKAGE_NAME))) license_package_name:=$(strip $(LOCAL_LICENSE_PACKAGE_NAME)) else license_package_name:= endif ifneq (,$(strip $(LOCAL_LICENSE_INSTALL_MAP))) install_map:=$(strip $(LOCAL_LICENSE_INSTALL_MAP)) else install_map:= endif ifneq (,$(strip $(LOCAL_LICENSE_KINDS))) license_kinds:=$(strip $(LOCAL_LICENSE_KINDS)) else license_kinds:=legacy_by_exception_only endif ifneq (,$(strip $(LOCAL_LICENSE_CONDITIONS))) license_conditions:=$(strip $(LOCAL_LICENSE_CONDITIONS)) else license_conditions:=by_exception_only endif is_container:=$(strip $(LOCAL_MODULE_IS_CONTAINER)) ifeq (,$(is_container)) ifneq (,$(strip $(filter %.zip %.tar %.tgz %.tar.gz %.apk %.img %.srcszip %.apex, $(LOCAL_BUILT_MODULE)))) is_container:=true else is_container:=false endif else ifneq (,$(strip $(filter-out true false,$(is_container)))) $(error Unrecognized value '$(is_container)' for LOCAL_MODULE_IS_CONTAINER) endif ifeq (true,$(is_container)) # Include shared libraries' notices for "container" types, but not for binaries etc. notice_deps := \ $(strip \ $(foreach d, \ $(LOCAL_REQUIRED_MODULES) \ $(LOCAL_STATIC_LIBRARIES) \ $(LOCAL_WHOLE_STATIC_LIBRARIES) \ $(LOCAL_SHARED_LIBRARIES) \ $(LOCAL_DYLIB_LIBRARIES) \ $(LOCAL_RLIB_LIBRARIES) \ $(LOCAL_PROC_MACRO_LIBRARIES) \ $(LOCAL_HEADER_LIBRARIES) \ $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(LOCAL_JAVA_LIBRARIES) \ $(LOCAL_JNI_SHARED_LIBRARIES) \ ,$(subst :,_,$(d)):static \ ) \ ) else notice_deps := \ $(strip \ $(foreach d, \ $(LOCAL_REQUIRED_MODULES) \ $(LOCAL_STATIC_LIBRARIES) \ $(LOCAL_WHOLE_STATIC_LIBRARIES) \ $(LOCAL_RLIB_LIBRARIES) \ $(LOCAL_PROC_MACRO_LIBRARIES) \ $(LOCAL_HEADER_LIBRARIES) \ $(LOCAL_STATIC_JAVA_LIBRARIES) \ ,$(subst :,_,$(d)):static \ )$(foreach d, \ $(LOCAL_SHARED_LIBRARIES) \ $(LOCAL_DYLIB_LIBRARIES) \ $(LOCAL_JAVA_LIBRARIES) \ $(LOCAL_JNI_SHARED_LIBRARIES) \ ,$(subst :,_,$(d)):dynamic \ ) \ ) endif ifeq ($(LOCAL_IS_HOST_MODULE),true) notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_HOST_REQUIRED_MODULES),$(subst :,_,$(d)):static)) else notice_deps := $(strip $(notice_deps) $(foreach d,$(LOCAL_TARGET_REQUIRED_MODULES),$(subst :,_,$(d)):static)) endif ALL_MODULES.$(my_register_name).DELAYED_META_LIC := $(strip $(ALL_MODULES.$(my_register_name).DELAYED_META_LIC) $(module_license_metadata)) ALL_MODULES.$(my_register_name).LICENSE_PACKAGE_NAME := $(strip $(license_package_name)) ALL_MODULES.$(my_register_name).MODULE_TYPE := $(strip $(ALL_MODULES.$(my_register_name).MODULE_TYPE) $(LOCAL_MODULE_TYPE)) ALL_MODULES.$(my_register_name).MODULE_CLASS := $(strip $(ALL_MODULES.$(my_register_name).MODULE_CLASS) $(LOCAL_MODULE_CLASS)) ALL_MODULES.$(my_register_name).LICENSE_KINDS := $(ALL_MODULES.$(my_register_name).LICENSE_KINDS) $(license_kinds) ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS := $(ALL_MODULES.$(my_register_name).LICENSE_CONDITIONS) $(license_conditions) ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP := $(ALL_MODULES.$(my_register_name).LICENSE_INSTALL_MAP) $(install_map) ALL_MODULES.$(my_register_name).NOTICE_DEPS := $(ALL_MODULES.$(my_register_name).NOTICE_DEPS) $(notice_deps) ALL_MODULES.$(my_register_name).IS_CONTAINER := $(strip $(filter-out false,$(ALL_MODULES.$(my_register_name).IS_CONTAINER) $(is_container))) ALL_MODULES.$(my_register_name).PATH := $(strip $(ALL_MODULES.$(my_register_name).PATH) $(local_path)) ifdef notice_file ALL_MODULES.$(my_register_name).NOTICES := $(ALL_MODULES.$(my_register_name).NOTICES) $(notice_file) endif # notice_file endif ================================================ FILE: core/os_licensing.mk ================================================ ifeq ($(TARGET_BUILD_APPS),) .PHONY: systemlicense systemlicense: $(call corresponding-license-metadata, $(SYSTEM_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(SYSTEM_NOTICE_DEPS)) SYSTEM_NOTICE_DEPS += $(UNMOUNTED_NOTICE_DEPS) $(UNMOUNTED_NOTICE_VENDOR_DEPS) $(eval $(call xml-notice-rule,$(target_notice_file_xml_gz),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS))) $(eval $(call text-notice-rule,$(target_notice_file_txt),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_notice_html_or_xml_gz): $(target_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_notice_html_or_xml_gz)) endif endif .PHONY: vendorlicense vendorlicense: $(call corresponding-license-metadata, $(VENDOR_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(VENDOR_NOTICE_DEPS)) VENDOR_NOTICE_DEPS += $(UNMOUNTED_NOTICE_VENDOR_DEPS) $(eval $(call text-notice-rule,$(target_vendor_notice_file_txt),"Vendor image", \ "Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:", \ $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_vendor_notice_file_xml_gz),"Vendor image", \ "Notices for files contained in all filesystem images except system/system_ext/product/odm/vendor_dlkm/odm_dlkm in this directory:", \ $(VENDOR_NOTICE_DEPS),$(VENDOR_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_vendor_notice_xml_gz): $(target_vendor_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_vendor_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_vendor_notice_xml_gz)) endif endif .PHONY: odmlicense odmlicense: $(call corresponding-license-metadata, $(ODM_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(ODM_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_odm_notice_file_txt),"ODM filesystem image", \ "Notices for files contained in the odm filesystem image in this directory:", \ $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_odm_notice_file_xml_gz),"ODM filesystem image", \ "Notices for files contained in the odm filesystem image in this directory:", \ $(ODM_NOTICE_DEPS),$(ODM_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_odm_notice_xml_gz): $(target_odm_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_odm_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_odm_notice_xml_gz)) endif endif .PHONY: oemlicense oemlicense: $(call corresponding-license-metadata, $(OEM_NOTICE_DEPS)) reportmissinglicenses .PHONY: productlicense productlicense: $(call corresponding-license-metadata, $(PRODUCT_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(PRODUCT_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_product_notice_file_txt),"Product image", \ "Notices for files contained in the product filesystem image in this directory:", \ $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_product_notice_file_xml_gz),"Product image", \ "Notices for files contained in the product filesystem image in this directory:", \ $(PRODUCT_NOTICE_DEPS),$(PRODUCT_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_product_notice_xml_gz): $(target_product_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_product_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_product_notice_xml_gz)) endif endif .PHONY: systemextlicense systemextlicense: $(call corresponding-license-metadata, $(SYSTEM_EXT_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(SYSTEM_EXT_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_system_ext_notice_file_txt),"System_ext image", \ "Notices for files contained in the system_ext filesystem image in this directory:", \ $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_system_ext_notice_file_xml_gz),"System_ext image", \ "Notices for files contained in the system_ext filesystem image in this directory:", \ $(SYSTEM_EXT_NOTICE_DEPS),$(SYSTEM_EXT_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_system_ext_notice_xml_gz): $(target_system_ext_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_system_ext_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_system_ext_notice_xml_gz)) endif endif .PHONY: vendor_dlkmlicense vendor_dlkmlicense: $(call corresponding-license-metadata, $(VENDOR_DLKM_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(VENDOR_DLKM_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_vendor_dlkm_notice_file_txt),"Vendor_dlkm image", \ "Notices for files contained in the vendor_dlkm filesystem image in this directory:", \ $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_vendor_dlkm_notice_file_xml_gz),"Vendor_dlkm image", \ "Notices for files contained in the vendor_dlkm filesystem image in this directory:", \ $(VENDOR_DLKM_NOTICE_DEPS),$(VENDOR_DLKM_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_vendor_dlkm_notice_xml_gz): $(target_vendor_dlkm_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_vendor_dlkm_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_vendor_dlkm_notice_xml_gz)) endif endif .PHONY: odm_dlkmlicense odm_dlkmlicense: $(call corresponding-license-metadata, $(ODM_DLKM_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(ODM_DLKM_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_odm_dlkm_notice_file_txt),"ODM_dlkm filesystem image", \ "Notices for files contained in the odm_dlkm filesystem image in this directory:", \ $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_odm_dlkm_notice_file_xml_gz),"ODM_dlkm filesystem image", \ "Notices for files contained in the odm_dlkm filesystem image in this directory:", \ $(ODM_DLKM_NOTICE_DEPS),$(ODM_DLKM_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_odm_dlkm_notice_xml_gz): $(target_odm_dlkm_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_odm_dlkm_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_odm_dlkm_notice_xml_gz)) endif endif .PHONY: system_dlkmlicense system_dlkmlicense: $(call corresponding-license-metadata, $(SYSTEM_DLKM_NOTICE_DEPS)) reportmissinglicenses ifneq (,$(SYSTEM_DLKM_NOTICE_DEPS)) $(eval $(call text-notice-rule,$(target_system_dlkm_notice_file_txt),"System_dlkm filesystem image", \ "Notices for files contained in the system_dlkm filesystem image in this directory:", \ $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS))) $(eval $(call xml-notice-rule,$(target_system_dlkm_notice_file_xml_gz),"System_dlkm filesystem image", \ "Notices for files contained in the system_dlkm filesystem image in this directory:", \ $(SYSTEM_DLKM_NOTICE_DEPS),$(SYSTEM_DLKM_NOTICE_DEPS))) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(installed_system_dlkm_notice_xml_gz): $(target_system_dlkm_notice_file_xml_gz) $(copy-file-to-target) endif $(call declare-1p-target,$(target_system_dlkm_notice_file_xml_gz)) ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true) $(call declare-1p-target,$(installed_sysetm_dlkm_notice_xml_gz)) endif endif endif # not TARGET_BUILD_APPS ================================================ FILE: core/pack_dyn_relocs_setup.mk ================================================ ############################################################# ## Set up my_pack_module_relocations ## Input variables: ## DISABLE_RELOCATION_PACKER, ## LOCAL_PACK_MODULE_RELOCATIONS*, ## *TARGET_PACK_MODULE_RELOCATIONS, ## LOCAL_MODULE_CLASS, HOST_OS ## LOCAL_IS_HOST_MODULE ## Output variables: ## my_pack_module_relocations, if false skip relocation_packer ############################################################# my_pack_module_relocations := false ifneq ($(DISABLE_RELOCATION_PACKER),true) my_pack_module_relocations := $(firstword \ $(LOCAL_PACK_MODULE_RELOCATIONS_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) \ $(LOCAL_PACK_MODULE_RELOCATIONS)) endif ifeq ($(my_pack_module_relocations),) my_pack_module_relocations := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PACK_MODULE_RELOCATIONS) endif # Do not pack relocations for executables. Because packing results in # non-zero p_vaddr which causes kernel to load executables to lower # address (starting at 0x8000) http://b/20665974 ifeq ($(filter SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),) my_pack_module_relocations := false endif ifdef LOCAL_IS_HOST_MODULE # Do not pack relocations on host modules my_pack_module_relocations := false endif # Lld relocation packing cannot be enabled for binaries before Android Pie. ifneq ($(LOCAL_SDK_VERSION),) ifneq ($(LOCAL_SDK_VERSION),current) ifeq ($(call math_lt,$(LOCAL_SDK_VERSION),28),true) my_pack_module_relocations := false endif endif endif ================================================ FILE: core/package.mk ================================================ # We don't automatically set up rules to build packages for both # TARGET_ARCH and TARGET_2ND_ARCH. # To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32". $(call record-module-type,PACKAGE) my_prefix := TARGET_ include $(BUILD_SYSTEM)/multilib.mk ifeq ($(TARGET_SUPPORTS_32_BIT_APPS)|$(TARGET_SUPPORTS_64_BIT_APPS),true|true) # packages default to building for either architecture, # the preferred if its supported, otherwise the non-preferred. else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true) # only 64-bit apps supported ifeq ($(filter $(my_module_multilib),64 both first),$(my_module_multilib)) # if my_module_multilib was 64, both, first, or unset, build for 64-bit my_module_multilib := 64 else # otherwise don't build this app my_module_multilib := none endif else # only 32-bit apps supported ifeq ($(filter $(my_module_multilib),32 both),$(my_module_multilib)) # if my_module_multilib was 32, both, or unset, build for 32-bit my_module_multilib := 32 else ifeq ($(my_module_multilib),first) ifndef TARGET_IS_64_BIT # if my_module_multilib was first and this is a 32-bit build, build for # 32-bit my_module_multilib := 32 else # if my_module_multilib was first and this is a 64-bit build, don't build # this app my_module_multilib := none endif else # my_module_mulitlib was 64 or none, don't build this app my_module_multilib := none endif endif LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true LOCAL_2ND_ARCH_VAR_PREFIX := # check if preferred arch is supported include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # first arch is supported include $(BUILD_SYSTEM)/package_internal.mk else ifneq (,$(TARGET_2ND_ARCH)) LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) # check if non-preferred arch is supported include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # secondary arch is supported include $(BUILD_SYSTEM)/package_internal.mk endif endif # TARGET_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := my_module_arch_supported := ================================================ FILE: core/package_internal.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ########################################################### ## Standard rules for building an application package. ## ## Additional inputs from base_rules.make: ## LOCAL_PACKAGE_NAME: The name of the package; the directory ## will be called this. ## ## MODULE, MODULE_PATH, and MODULE_SUFFIX will ## be set for you. ########################################################### LOCAL_PACKAGE_NAME := $(strip $(LOCAL_PACKAGE_NAME)) ifeq ($(LOCAL_PACKAGE_NAME),) $(error $(LOCAL_PATH): Package modules must define LOCAL_PACKAGE_NAME) endif ifneq ($(strip $(LOCAL_MODULE_SUFFIX)),) $(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_SUFFIX) endif LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) $(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_STEM or LOCAL_BUILT_MODULE_STEM) endif ifneq ($(strip $(LOCAL_MODULE)),) $(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE) endif LOCAL_MODULE := $(LOCAL_PACKAGE_NAME) ifneq ($(strip $(LOCAL_MODULE_CLASS)),) $(error $(LOCAL_PATH): Package modules may not set LOCAL_MODULE_CLASS) endif LOCAL_MODULE_CLASS := APPS intermediates := $(call local-intermediates-dir) intermediates.COMMON := $(call local-intermediates-dir,COMMON) # Package LOCAL_MODULE_TAGS default to optional LOCAL_MODULE_TAGS := $(strip $(LOCAL_MODULE_TAGS)) ifeq ($(LOCAL_MODULE_TAGS),) LOCAL_MODULE_TAGS := optional endif ifeq ($(filter tests, $(LOCAL_MODULE_TAGS)),) # Force localization check if it's not tagged as tests. LOCAL_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) -z endif need_compile_asset := ifeq (,$(LOCAL_ASSET_DIR)) LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets else need_compile_asset := true endif # LOCAL_RESOURCE_DIR may point to resource generated during the build need_compile_res := ifeq (,$(LOCAL_RESOURCE_DIR)) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res else need_compile_res := true LOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d))) endif # If LOCAL_MODULE matches a rule in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES, # override the manifest package name by the (first) rule matched override_manifest_name := $(strip $(word 1,\ $(foreach rule,$(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES),\ $(eval _pkg_name_pat := $(call word-colon,1,$(rule)))\ $(eval _manifest_name_pat := $(call word-colon,2,$(rule)))\ $(if $(filter $(_pkg_name_pat),$(LOCAL_MODULE)),\ $(patsubst $(_pkg_name_pat),$(_manifest_name_pat),$(LOCAL_MODULE))\ )\ )\ )) ifneq (,$(override_manifest_name)) # Note: this can override LOCAL_MANIFEST_PACKAGE_NAME value set in Android.mk LOCAL_MANIFEST_PACKAGE_NAME := $(override_manifest_name) endif include $(BUILD_SYSTEM)/force_aapt2.mk # validate that app contains a manifest file for aapt2 ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE))) ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml)) $(call pretty-error,App missing manifest file which is required by aapt2. \ Provide a manifest file by either setting LOCAL_MANIFEST_FILE in Android.mk \ or via a AndroidManifest.xml in this directory) endif endif # Process Support Library dependencies. include $(BUILD_SYSTEM)/support_libraries.mk # Determine whether auto-RRO is enabled for this package. enforce_rro_enabled := ifeq (,$(filter tests,$(LOCAL_MODULE_TAGS))) ifneq (,$(filter *, $(PRODUCT_ENFORCE_RRO_TARGETS))) # * means all system and system_ext APKs, so enable conditionally based on module path. # Note that base_rules.mk has not yet been included, so it's likely that only # one of LOCAL_MODULE_PATH and the LOCAL_X_MODULE flags has been set. ifeq (,$(LOCAL_MODULE_PATH)) non_rro_target_module := $(filter true,\ $(LOCAL_ODM_MODULE) \ $(LOCAL_OEM_MODULE) \ $(LOCAL_PRODUCT_MODULE) \ $(LOCAL_PROPRIETARY_MODULE) \ $(LOCAL_VENDOR_MODULE)) enforce_rro_enabled := $(if $(non_rro_target_module),,true) else ifneq ($(filter $(TARGET_OUT)/%,$(LOCAL_MODULE_PATH)),) enforce_rro_enabled := true endif else ifneq (,$(filter $(LOCAL_PACKAGE_NAME), $(PRODUCT_ENFORCE_RRO_TARGETS))) enforce_rro_enabled := true endif endif product_package_overlays := $(strip \ $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \ $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR))))) device_package_overlays := $(strip \ $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \ $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR))))) static_resource_overlays := runtime_resource_overlays_product := runtime_resource_overlays_vendor := ifdef enforce_rro_enabled ifneq ($(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS),) # The PRODUCT_ exclusion variable applies to both inclusion variables.. static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(product_package_overlays)) static_resource_overlays += $(filter $(addsuffix %,$(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)),$(device_package_overlays)) endif runtime_resource_overlays_product := $(filter-out $(static_resource_overlays),$(product_package_overlays)) runtime_resource_overlays_vendor := $(filter-out $(static_resource_overlays),$(device_package_overlays)) else static_resource_overlays := $(product_package_overlays) $(device_package_overlays) endif # Add the static overlays. Auto-RRO is created later, as it depends on # other logic in this file. LOCAL_RESOURCE_DIR := $(static_resource_overlays) $(LOCAL_RESOURCE_DIR) all_assets := $(strip \ $(foreach dir, $(LOCAL_ASSET_DIR), \ $(addprefix $(dir)/, \ $(patsubst assets/%,%, \ $(call find-subdir-assets, $(dir)) \ ) \ ) \ )) ifneq ($(all_assets),) need_compile_asset := true endif my_res_package := # In aapt2 the last takes precedence. my_resource_dirs := $(call reverse-list,$(LOCAL_RESOURCE_DIR)) my_res_dir := my_overlay_res_dirs := ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) # If we are using static android libraries, every source file becomes an overlay. # This is to emulate old AAPT behavior which simulated library support. my_res_dir := my_overlay_res_dirs := $(my_resource_dirs) else # Without static libraries, the first directory is our directory, which can then be # overlaid by the rest. (First directory in my_resource_dirs is last directory in # $(LOCAL_RESOURCE_DIR) due to it being reversed. my_res_dir := $(firstword $(my_resource_dirs)) my_overlay_res_dirs := $(wordlist 2,999,$(my_resource_dirs)) endif my_overlay_resources := $(strip \ $(foreach d,$(my_overlay_res_dirs),\ $(addprefix $(d)/, \ $(call find-subdir-assets,$(d))))) my_res_resources := $(if $(my_res_dir),$(strip \ $(addprefix $(my_res_dir)/, \ $(call find-subdir-assets,$(my_res_dir))))) all_resources := $(strip $(my_res_resources) $(my_overlay_resources)) # The linked resource package. my_res_package := $(intermediates.COMMON)/package-res.apk LOCAL_INTERMEDIATE_TARGETS += $(my_res_package) my_bundle_module := $(intermediates.COMMON)/base.zip LOCAL_INTERMEDIATE_TARGETS += $(my_bundle_module) # Always run aapt2, because we need to at least compile the AndroidManifest.xml. need_compile_res := true ifneq ($(all_resources),) need_compile_res := true endif all_res_assets := $(strip $(all_assets) $(all_resources)) # If no assets or resources were found, clear the directory variables so # we don't try to build them. ifneq (true,$(need_compile_asset)) LOCAL_ASSET_DIR:= endif ifneq (true,$(need_compile_res)) LOCAL_RESOURCE_DIR:= R_file_stamp := else # Make sure that R_file_stamp inherits the proper PRIVATE vars. # If R.stamp moves, be sure to update the framework makefile, # which has intimate knowledge of its location. R_file_stamp := $(intermediates.COMMON)/src/R.stamp LOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp) endif ifdef LOCAL_COMPRESSED_MODULE ifneq (true,$(LOCAL_COMPRESSED_MODULE)) $(call pretty-error, Unknown value for LOCAL_COMPRESSED_MODULE $(LOCAL_COMPRESSED_MODULE)) endif endif ifdef LOCAL_COMPRESSED_MODULE PACKAGES.$(LOCAL_PACKAGE_NAME).COMPRESSED := gz LOCAL_BUILT_MODULE_STEM := package.apk.gz LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk.gz else # !LOCAL_COMPRESSED_MODULE LOCAL_BUILT_MODULE_STEM := package.apk LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk endif LOCAL_PROGUARD_ENABLED:=$(strip $(LOCAL_PROGUARD_ENABLED)) ifndef LOCAL_PROGUARD_ENABLED ifneq ($(DISABLE_PROGUARD),true) LOCAL_PROGUARD_ENABLED :=full endif endif ifeq ($(LOCAL_PROGUARD_ENABLED),disabled) # the package explicitly request to disable proguard. LOCAL_PROGUARD_ENABLED := endif proguard_options_file := ifneq ($(LOCAL_PROGUARD_ENABLED),custom) ifeq ($(need_compile_res),true) proguard_options_file := $(intermediates.COMMON)/proguard_options endif # need_compile_res endif # !custom LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS) LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file) ifeq (true,$(EMMA_INSTRUMENT)) ifndef LOCAL_EMMA_INSTRUMENT # No jacoco for test apks. ifeq (,$(LOCAL_INSTRUMENTATION_FOR)) LOCAL_EMMA_INSTRUMENT := true endif # No test apk endif # LOCAL_EMMA_INSTRUMENT is not set else LOCAL_EMMA_INSTRUMENT := false endif # EMMA_INSTRUMENT is true ifeq (true,$(LOCAL_EMMA_INSTRUMENT)) ifeq (true,$(EMMA_INSTRUMENT_STATIC)) ifneq ($(LOCAL_SRC_FILES)$(LOCAL_SRCJARS)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),) # Only add jacocoagent if the package contains some java code LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent # Exclude jacoco classes from proguard LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags endif # Contains java code else ifdef LOCAL_SDK_VERSION ifdef TARGET_BUILD_APPS # In unbundled build, merge the coverage library into the apk. ifneq ($(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),) # Only add jacocoagent if the package contains some java code LOCAL_STATIC_JAVA_LIBRARIES += jacocoagent # Exclude jacoco classes from proguard LOCAL_PROGUARD_FLAGS += -include $(BUILD_SYSTEM)/proguard.jacoco.flags LOCAL_PROGUARD_FLAGS_DEPS += $(BUILD_SYSTEM)/proguard.jacoco.flags endif # Contains java code endif # TARGET_BUILD_APPS endif # LOCAL_SDK_VERSION endif # EMMA_INSTRUMENT_STATIC endif # LOCAL_EMMA_INSTRUMENT rs_compatibility_jni_libs := # If the module is a compressed module, we don't pre-opt it because its final # installation location will be the data partition. ifdef LOCAL_COMPRESSED_MODULE LOCAL_DEX_PREOPT := false endif # Default to use uncompressed native libraries in APKs if minSdkVersion >= marshmallow ifndef LOCAL_USE_EMBEDDED_NATIVE_LIBS LOCAL_USE_EMBEDDED_NATIVE_LIBS := $(call math_gt_or_eq, \ $(patsubst $(PLATFORM_VERSION_CODENAME),100,$(call module-min-sdk-version)),23) endif include $(BUILD_SYSTEM)/android_manifest.mk resource_export_package := include $(BUILD_SYSTEM)/java_renderscript.mk include $(BUILD_SYSTEM)/aapt_flags.mk ifeq ($(need_compile_res),true) ############################### ## APK splits built_apk_splits := installed_apk_splits := my_apk_split_configs := ifdef LOCAL_PACKAGE_SPLITS ifdef LOCAL_COMPRESSED_MODULE $(error $(LOCAL_MODULE): LOCAL_COMPRESSED_MODULE is not currently supported for split installs) endif # LOCAL_COMPRESSED_MODULE my_apk_split_configs := $(LOCAL_PACKAGE_SPLITS) my_split_suffixes := $(subst $(comma),_,$(my_apk_split_configs)) built_apk_splits := $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk) endif $(R_file_stamp) $(my_res_package): PRIVATE_AAPT_FLAGS := $(filter-out --legacy,$(LOCAL_AAPT_FLAGS)) $(R_file_stamp) $(my_res_package): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS) $(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME) $(R_file_stamp) $(my_res_package): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR) ############################### ## AAPT2 my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res ifneq (,$(filter-out current,$(renderscript_target_api))) ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true) my_generated_res_zips := $(rs_generated_res_zip) endif # renderscript_target_api < 21 endif # renderscript_target_api is set my_asset_dirs := $(LOCAL_ASSET_DIR) my_full_asset_paths := $(all_assets) # Add AAPT2 link specific flags. ifndef LOCAL_AAPT_NAMESPACES $(my_res_package): PRIVATE_AAPT_FLAGS += --no-static-lib-packages endif include $(BUILD_SYSTEM)/aapt2.mk endif # need_compile_res my_dex_jar := $(intermediates.COMMON)/dex.jar called_from_package_internal := true ################################# include $(BUILD_SYSTEM)/java.mk ################################# called_from_package_internal := ifeq ($(need_compile_res),true) # Other modules should depend on the BUILT module if # they want to use this module's R.java file. $(LOCAL_BUILT_MODULE): $(R_file_stamp) ifneq ($(full_classes_jar),) # The R.java file must exist by the time the java source # list is generated $(java_source_list_file): $(R_file_stamp) endif endif # need_compile_res LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) ifeq ($(LOCAL_SDK_RES_VERSION),) LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) endif $(LOCAL_INTERMEDIATE_TARGETS): \ PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) framework_res_package_export := ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) # Most packages should link against the resources defined by framework-res. # Even if they don't have their own resources, they may use framework # resources. ifeq ($(LOCAL_SDK_RES_VERSION),core_current) # core_current doesn't contain any framework resources. else ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) # for released sdk versions, the platform resources were built into android.jar. framework_res_package_export := \ $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) else # LOCAL_SDK_RES_VERSION framework_res_package_export := \ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk endif # LOCAL_SDK_RES_VERSION endif # LOCAL_NO_STANDARD_LIBRARIES all_library_res_package_exports := \ $(framework_res_package_export) \ $(foreach lib,$(LOCAL_RES_LIBRARIES),\ $(call intermediates-dir-for,APPS,$(lib),,COMMON)/package-export.apk) all_library_res_package_export_deps := \ $(framework_res_package_export) \ $(foreach lib,$(LOCAL_RES_LIBRARIES),\ $(call intermediates-dir-for,APPS,$(lib),,COMMON)/src/R.stamp) $(resource_export_package) $(R_file_stamp) $(LOCAL_BUILT_MODULE): $(all_library_res_package_export_deps) $(LOCAL_INTERMEDIATE_TARGETS): \ PRIVATE_AAPT_INCLUDES := $(all_library_res_package_exports) $(my_res_package) : $(all_library_res_package_export_deps) ifneq ($(full_classes_jar),) $(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex) # Use the jarjar processed arhive as the initial package file. $(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) $(LOCAL_BUILT_MODULE): $(built_dex) $(full_classes_pre_proguard_jar) else $(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(LOCAL_BUILT_MODULE): PRIVATE_SOURCE_ARCHIVE := endif # full_classes_jar include $(BUILD_SYSTEM)/install_jni_libs.mk # Pick a key to sign the package with. If this package hasn't specified # an explicit certificate, use the default. # Secure release builds will have their packages signed after the fact, # so it's ok for these private keys to be in the clear. ifeq ($(LOCAL_CERTIFICATE),) LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) endif ifeq ($(LOCAL_CERTIFICATE),EXTERNAL) # The special value "EXTERNAL" means that we will sign it with the # default devkey, apply predexopt, but then expect the final .apk # (after dexopting) to be signed by an outside tool. LOCAL_CERTIFICATE := $(DEFAULT_SYSTEM_DEV_CERTIFICATE) PACKAGES.$(LOCAL_PACKAGE_NAME).EXTERNAL_KEY := 1 endif # If this is not an absolute certificate, assign it to a generic one. ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./) LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE) endif include $(BUILD_SYSTEM)/app_certificate_validate.mk private_key := $(LOCAL_CERTIFICATE).pk8 certificate := $(LOCAL_CERTIFICATE).x509.pem additional_certificates := $(foreach c,$(LOCAL_ADDITIONAL_CERTIFICATES), $(c).x509.pem $(c).pk8) $(LOCAL_BUILT_MODULE): $(private_key) $(certificate) $(SIGNAPK_JAR) $(SIGNAPK_JNI_LIBRARY_PATH) $(LOCAL_BUILT_MODULE): PRIVATE_PRIVATE_KEY := $(private_key) $(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE := $(certificate) PACKAGES.$(LOCAL_PACKAGE_NAME).PRIVATE_KEY := $(private_key) PACKAGES.$(LOCAL_PACKAGE_NAME).CERTIFICATE := $(certificate) $(LOCAL_BUILT_MODULE): $(additional_certificates) $(LOCAL_BUILT_MODULE): PRIVATE_ADDITIONAL_CERTIFICATES := $(additional_certificates) $(LOCAL_BUILT_MODULE): $(LOCAL_CERTIFICATE_LINEAGE) $(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE_LINEAGE := $(LOCAL_CERTIFICATE_LINEAGE) $(LOCAL_BUILT_MODULE): PRIVATE_ROTATION_MIN_SDK_VERSION := $(LOCAL_ROTATION_MIN_SDK_VERSION) # Set a actual_partition_tag (calculated in base_rules.mk) for the package. PACKAGES.$(LOCAL_PACKAGE_NAME).PARTITION := $(actual_partition_tag) # Define the rule to build the actual package. # PRIVATE_JNI_SHARED_LIBRARIES is a list of :. $(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis) # PRIVATE_JNI_SHARED_LIBRARIES_ABI is a list of ABI names. $(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis) ifneq ($(TARGET_BUILD_APPS),) # Include all resources for unbundled apps. LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true endif ifeq ($(LOCAL_AAPT_INCLUDE_ALL_RESOURCES),true) $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG := $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := else $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_CONFIG := $(PRODUCT_AAPT_CONFIG) ifdef LOCAL_PACKAGE_SPLITS $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := else $(my_res_package) $(LOCAL_BUILT_MODULE): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := $(PRODUCT_AAPT_PREF_CONFIG) endif endif # Run veridex on product, system_ext and vendor modules. # We skip it for unbundled app builds where we cannot build veridex. module_run_appcompat := ifeq (true,$(non_system_module)) ifeq (,$(TARGET_BUILD_APPS)) # ! unbundled app build ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) module_run_appcompat := true endif endif endif ifeq ($(module_run_appcompat),true) $(LOCAL_BUILT_MODULE) : $(appcompat-files) $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) endif $(LOCAL_BUILT_MODULE): PRIVATE_RESOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/resources $(LOCAL_BUILT_MODULE) : $(jni_shared_libraries) $(LOCAL_BUILT_MODULE) : $(JAR_ARGS) $(SOONG_ZIP) $(MERGE_ZIPS) $(ZIP2ZIP) $(LOCAL_BUILT_MODULE): PRIVATE_RES_PACKAGE := $(my_res_package) $(LOCAL_BUILT_MODULE) : $(my_res_package) $(AAPT2) ifdef LOCAL_COMPRESSED_MODULE $(LOCAL_BUILT_MODULE) : $(GZIP) endif ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) $(LOCAL_BUILT_MODULE) : $(ZIP2ZIP) endif ifeq ($(full_classes_jar),) # We don't build jar, need to add the Java resources here. $(LOCAL_BUILT_MODULE): $(java_resource_sources) endif $(LOCAL_BUILT_MODULE): PRIVATE_USE_EMBEDDED_NATIVE_LIBS := $(LOCAL_USE_EMBEDDED_NATIVE_LIBS) $(LOCAL_BUILT_MODULE): @echo "target Package: $(PRIVATE_MODULE) ($@)" rm -rf $@.parts mkdir -p $@.parts cp -f $(PRIVATE_RES_PACKAGE) $@.parts/apk.zip ifneq ($(jni_shared_libraries),) $(call create-jni-shared-libs-package,$@.parts/jni.zip,$(PRIVATE_USE_EMBEDDED_NATIVE_LIBS)) endif ifeq ($(full_classes_jar),) # We don't build jar, need to add the Java resources here. $(if $(PRIVATE_EXTRA_JAR_ARGS),$(call create-java-resources-jar,$@.parts/res.zip)) else # full_classes_jar $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) endif # full_classes_jar $(MERGE_ZIPS) $@ $@.parts/*.zip rm -rf $@.parts ifeq (true, $(LOCAL_UNCOMPRESS_DEX)) @# No need to align, sign-package below will do it. $(uncompress-dexs) endif # Run appcompat before signing. ifeq ($(module_run_appcompat),true) $(appcompat-header) $(run-appcompat) endif # module_run_appcompat $(sign-package) ifdef LOCAL_COMPRESSED_MODULE $(compress-package) endif # LOCAL_COMPRESSED_MODULE my_package_res_pb := $(intermediates.COMMON)/package-res.pb.apk $(my_package_res_pb): $(my_res_package) $(AAPT2) $(AAPT2) convert --output-format proto $< -o $@ $(my_bundle_module): $(my_package_res_pb) $(my_bundle_module): PRIVATE_RES_PACKAGE := $(my_package_res_pb) $(my_bundle_module): $(jni_shared_libraries) $(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis) $(my_bundle_module): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis) ifneq ($(full_classes_jar),) $(my_bundle_module): PRIVATE_DEX_FILE := $(built_dex) # Use the jarjar processed archive as the initial package file. $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE := $(full_classes_pre_proguard_jar) $(my_bundle_module): $(built_dex) else $(my_bundle_module): PRIVATE_DEX_FILE := $(my_bundle_module): PRIVATE_SOURCE_ARCHIVE := # We don't build jar, need to add the Java resources here. $(my_bundle_module): $(java_resource_sources) endif # full_classes_jar $(my_bundle_module): $(MERGE_ZIPS) $(SOONG_ZIP) $(ZIP2ZIP) @echo "target Bundle: $(PRIVATE_MODULE) ($@)" rm -rf $@.parts mkdir -p $@.parts $(ZIP2ZIP) -i $(PRIVATE_RES_PACKAGE) -o $@.parts/apk.zip AndroidManifest.xml:manifest/AndroidManifest.xml resources.pb "res/**/*" "assets/**/*" ifneq ($(jni_shared_libraries),) $(call create-jni-shared-libs-package,$@.parts/jni.zip) endif ifeq ($(full_classes_jar),) # We don't build jar, need to add the Java resources here. $(if $(PRIVATE_EXTRA_JAR_ARGS),\ $(call create-java-resources-jar,$@.parts/res.zip) && \ $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp "**/*:root/" && \ mv -f $@.parts/res.zip.tmp $@.parts/res.zip) else # full_classes_jar $(call create-dex-jar,$@.parts/dex.zip,$(PRIVATE_DEX_FILE)) $(ZIP2ZIP) -i $@.parts/dex.zip -o $@.parts/dex.zip.tmp "classes*.dex:dex/" mv -f $@.parts/dex.zip.tmp $@.parts/dex.zip $(call extract-resources-jar,$@.parts/res.zip,$(PRIVATE_SOURCE_ARCHIVE)) $(ZIP2ZIP) -i $@.parts/res.zip -o $@.parts/res.zip.tmp "**/*:root/" mv -f $@.parts/res.zip.tmp $@.parts/res.zip endif # full_classes_jar $(MERGE_ZIPS) $@ $@.parts/*.zip rm -rf $@.parts ALL_MODULES.$(my_register_name).BUNDLE := $(my_bundle_module) ifdef TARGET_BUILD_APPS ifdef LOCAL_DPI_VARIANTS $(call pretty-error,Building DPI-specific APKs is no longer supported) endif endif ############################### ## Rule to build a jar containing dex files to dexpreopt without waiting for ## the APK ifdef LOCAL_DEX_PREOPT $(my_dex_jar): PRIVATE_DEX_FILE := $(built_dex) $(my_dex_jar): $(built_dex) $(SOONG_ZIP) $(hide) mkdir -p $(dir $@) && rm -f $@ $(call create-dex-jar,$@,$(PRIVATE_DEX_FILE)) endif ############################### ## APK splits ifdef LOCAL_PACKAGE_SPLITS # The splits should have been built in the same command building the base apk. # This rule just runs signing. # Note that we explicily check the existence of the split apk and remove the # built base apk if the split apk isn't there. # That way the build system will rerun the aapt after the user changes the splitting parameters. $(built_apk_splits): PRIVATE_PRIVATE_KEY := $(private_key) $(built_apk_splits): PRIVATE_CERTIFICATE := $(certificate) $(built_apk_splits) : $(intermediates)/%.apk : $(LOCAL_BUILT_MODULE) $(hide) if [ ! -f $@ ]; then \ echo 'No $@ generated, check your apk splitting parameters.' 1>&2; \ rm $<; exit 1; \ fi $(sign-package) # Rules to install the splits installed_apk_splits := $(foreach s,$(my_split_suffixes),$(my_module_path)/$(LOCAL_MODULE)_$(s).apk) $(installed_apk_splits) : $(my_module_path)/$(LOCAL_MODULE)_%.apk : $(intermediates)/package_%.apk @echo "Install: $@" $(copy-file-to-new-target) # Register the additional built and installed files. ALL_MODULES.$(my_register_name).INSTALLED += $(installed_apk_splits) ALL_MODULES.$(my_register_name).BUILT_INSTALLED += \ $(foreach s,$(my_split_suffixes),$(intermediates)/package_$(s).apk:$(my_module_path)/$(LOCAL_MODULE)_$(s).apk) # Make sure to install the splits when you run "make ". $(my_all_targets): $(installed_apk_splits) ifdef LOCAL_COMPATIBILITY_SUITE $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(foreach s,$(my_split_suffixes),\ $(call compat-copy-pair,$(intermediates)/package_$(s).apk,$(dir)/$(LOCAL_MODULE)_$(s).apk))))) $(call create-suite-dependencies) endif # LOCAL_COMPATIBILITY_SUITE endif # LOCAL_PACKAGE_SPLITS # Save information about this package PACKAGES.$(LOCAL_PACKAGE_NAME).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) PACKAGES.$(LOCAL_PACKAGE_NAME).RESOURCE_FILES := $(all_resources) ifneq ($(LOCAL_MODULE_STEM),) PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) else PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) endif PACKAGES := $(PACKAGES) $(LOCAL_PACKAGE_NAME) # Reset internal variables. all_res_assets := ifneq (,$(runtime_resource_overlays_product)$(runtime_resource_overlays_vendor)) ifdef LOCAL_EXPORT_PACKAGE_RESOURCES enforce_rro_use_res_lib := true else enforce_rro_use_res_lib := false endif ifdef LOCAL_MANIFEST_PACKAGE_NAME enforce_rro_is_manifest_package_name := true enforce_rro_manifest_package_info := $(LOCAL_MANIFEST_PACKAGE_NAME) else enforce_rro_is_manifest_package_name := false enforce_rro_manifest_package_info := $(full_android_manifest) endif ifdef runtime_resource_overlays_product $(call append_enforce_rro_sources, \ $(my_register_name), \ $(enforce_rro_is_manifest_package_name), \ $(enforce_rro_manifest_package_info), \ $(enforce_rro_use_res_lib), \ $(runtime_resource_overlays_product), \ product \ ) endif ifdef runtime_resource_overlays_vendor $(call append_enforce_rro_sources, \ $(my_register_name), \ $(enforce_rro_is_manifest_package_name), \ $(enforce_rro_manifest_package_info), \ $(enforce_rro_use_res_lib), \ $(runtime_resource_overlays_vendor), \ vendor \ ) endif endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PACKAGE)) ================================================ FILE: core/packaging/flags.mk ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file is included by build/make/core/Makefile, and contains the logic for # the combined flags files. # # TODO: Should we do all of the images? _FLAG_PARTITIONS := product system system_ext vendor # ----------------------------------------------------------------- # Aconfig Flags # Create a summary file of build flags for a single partition # $(1): built aconfig flags file (out) # $(2): installed aconfig flags file (out) # $(3): the partition (in) # $(4): input aconfig files for the partition (in) define generate-partition-aconfig-flag-file $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1))) $(eval $(strip $(1)): PRIVATE_IN := $(strip $(4))) $(strip $(1)): $(ACONFIG) $(strip $(4)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \ --filter container:$(strip $(3))+state:ENABLED \ --filter container:$(strip $(3))+permission:READ_WRITE \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ echo -n > $$(PRIVATE_OUT) \ ) $(call copy-one-file, $(1), $(2)) endef # Create a summary file of build flags for each partition # $(1): built aconfig flags file (out) # $(2): installed aconfig flags file (out) # $(3): input aconfig files for the partition (in) define generate-global-aconfig-flag-file $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1))) $(eval $(strip $(1)): PRIVATE_IN := $(strip $(3))) $(strip $(1)): $(ACONFIG) $(strip $(3)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ echo -n > $$(PRIVATE_OUT) \ ) $(call copy-one-file, $(1), $(2)) endef define out-dir-for-partition $(TARGET_COPY_OUT_$(call to-upper,$(1))) endef # Get the module names suitable for ALL_MODULES.* variables that are installed # for a given container # $(1): container define register-names-for-container $(sort $(foreach m,$(product_MODULES),\ $(if $(filter $(PRODUCT_OUT)/$(call out-dir-for-partition,$(strip $(1)))/%, $(ALL_MODULES.$(m).INSTALLED)), \ $(m) ) \ )) endef $(foreach partition, $(_FLAG_PARTITIONS), \ $(eval aconfig_flag_summaries_protobuf.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig_flags.pb) \ $(eval $(call generate-partition-aconfig-flag-file, \ $(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \ $(aconfig_flag_summaries_protobuf.$(partition)), \ $(partition), \ $(sort \ $(foreach m, $(call register-names-for-container, $(partition)), \ $(ALL_MODULES.$(m).ACONFIG_FILES) \ ) \ ) \ )) \ ) # Collect the on-device flags into a single file, similar to all_aconfig_declarations. required_aconfig_flags_files := \ $(sort $(foreach partition, $(_FLAG_PARTITIONS), \ $(aconfig_flag_summaries_protobuf.$(partition)) \ )) .PHONY: device_aconfig_declarations device_aconfig_declarations: $(PRODUCT_OUT)/device_aconfig_declarations.pb $(eval $(call generate-global-aconfig-flag-file, \ $(TARGET_OUT_FLAGS)/device_aconfig_declarations.pb, \ $(PRODUCT_OUT)/device_aconfig_declarations.pb, \ $(sort $(required_aconfig_flags_files)) \ )) \ # Create a set of storage file for each partition # $(1): built aconfig flags storage package map file (out) # $(2): built aconfig flags storage flag map file (out) # $(3): built aconfig flags storage flag val file (out) # $(4): built aconfig flags storage flag info file (out) # $(5): installed aconfig flags storage package map file (out) # $(6): installed aconfig flags storage flag map file (out) # $(7): installed aconfig flags storage flag value file (out) # $(8): installed aconfig flags storage flag info file (out) # $(9): input aconfig files for the partition (in) # $(10): partition name define generate-partition-aconfig-storage-file $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1))) $(eval $(strip $(1)): PRIVATE_IN := $(strip $(9))) ifneq (,$(RELEASE_FINGERPRINT_ACONFIG_PACKAGES)) STORAGE_FILE_VERSION := 2 else STORAGE_FILE_VERSION := 1 endif $(strip $(1)): $(ACONFIG) $(strip $(9)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) create-storage --container $(10) --file package_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ ) touch $$(PRIVATE_OUT) $(eval $(strip $(2)): PRIVATE_OUT := $(strip $(2))) $(eval $(strip $(2)): PRIVATE_IN := $(strip $(9))) $(strip $(2)): $(ACONFIG) $(strip $(9)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) create-storage --container $(10) --file flag_map --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ ) touch $$(PRIVATE_OUT) $(eval $(strip $(3)): PRIVATE_OUT := $(strip $(3))) $(eval $(strip $(3)): PRIVATE_IN := $(strip $(9))) $(strip $(3)): $(ACONFIG) $(strip $(9)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) create-storage --container $(10) --file flag_val --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ ) touch $$(PRIVATE_OUT) $(eval $(strip $(4)): PRIVATE_OUT := $(strip $(4))) $(eval $(strip $(4)): PRIVATE_IN := $(strip $(9))) $(strip $(4)): $(ACONFIG) $(strip $(9)) mkdir -p $$(dir $$(PRIVATE_OUT)) $$(if $$(PRIVATE_IN), \ $$(ACONFIG) create-storage --container $(10) --file flag_info --out $$(PRIVATE_OUT) --version $$(STORAGE_FILE_VERSION) \ $$(addprefix --cache ,$$(PRIVATE_IN)), \ ) touch $$(PRIVATE_OUT) $(call copy-one-file, $(strip $(1)), $(5)) $(call copy-one-file, $(strip $(2)), $(6)) $(call copy-one-file, $(strip $(3)), $(7)) $(call copy-one-file, $(strip $(4)), $(8)) endef ifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true) $(foreach partition, $(_FLAG_PARTITIONS), \ $(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/package.map) \ $(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.map) \ $(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.val) \ $(eval aconfig_storage_flag_info.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.info) \ $(eval $(call generate-partition-aconfig-storage-file, \ $(TARGET_OUT_FLAGS)/$(partition)/package.map, \ $(TARGET_OUT_FLAGS)/$(partition)/flag.map, \ $(TARGET_OUT_FLAGS)/$(partition)/flag.val, \ $(TARGET_OUT_FLAGS)/$(partition)/flag.info, \ $(aconfig_storage_package_map.$(partition)), \ $(aconfig_storage_flag_map.$(partition)), \ $(aconfig_storage_flag_val.$(partition)), \ $(aconfig_storage_flag_info.$(partition)), \ $(aconfig_flag_summaries_protobuf.$(partition)), \ $(partition), \ )) \ ) endif # ----------------------------------------------------------------- # Install the ones we need for the configured product required_flags_files := \ $(sort $(foreach partition, $(_FLAG_PARTITIONS), \ $(build_flag_summaries.$(partition)) \ $(aconfig_flag_summaries_protobuf.$(partition)) \ $(aconfig_storage_package_map.$(partition)) \ $(aconfig_storage_flag_map.$(partition)) \ $(aconfig_storage_flag_val.$(partition)) \ $(aconfig_storage_flag_info.$(partition)) \ )) ALL_DEFAULT_INSTALLED_MODULES += $(required_flags_files) ALL_FLAGS_FILES := $(required_flags_files) # TODO: Remove .PHONY: flag-files flag-files: $(required_flags_files) # Clean up out-dir-for-partition:= register-names-for-container:= required_flags_files:= required_aconfig_flags_files:= $(foreach partition, $(_FLAG_PARTITIONS), \ $(eval build_flag_summaries.$(partition):=) \ $(eval aconfig_flag_summaries_protobuf.$(partition):=) \ $(eval aconfig_storage_package_map.$(partition):=) \ $(eval aconfig_storage_flag_map.$(partition):=) \ $(eval aconfig_storage_flag_val.$(partition):=) \ $(eval aconfig_storage_flag_info.$(partition):=) \ ) ================================================ FILE: core/pathmap.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # A central place to define mappings to paths, to avoid hard-coding # them in Android.mk files. Not meant for header file include directories, # despite the fact that it was historically used for that! # # If you want this for a library's header files, use LOCAL_EXPORT_C_INCLUDES # instead. Then users of the library don't have to do anything --- they'll # have the correct header files added to their include path automatically. # # # TODO: Allow each project to define stuff like this before the per-module # Android.mk files are included, so we don't need to have a big central # list. # # # A mapping from shorthand names to include directories. # pathmap_INCL := \ camera:system/media/camera/include \ frameworks-base:frameworks/base/include \ frameworks-native:frameworks/native/include \ libhardware:hardware/libhardware/include \ libhardware_legacy:hardware/libhardware_legacy/include \ libril:hardware/ril/include \ system-core:system/core/include \ audio:system/media/audio/include \ audio-effects:system/media/audio_effects/include \ audio-utils:system/media/audio_utils/include \ audio-route:system/media/audio_route/include \ wilhelm:frameworks/wilhelm/include \ wilhelm-ut:frameworks/wilhelm/src/ut \ mediandk:frameworks/av/media/ndk/ # # Returns the path to the requested module's include directory, # relative to the root of the source tree. Does not handle external # modules. # # $(1): a list of modules (or other named entities) to find the includes for # define include-path-for $(foreach n,$(1),$(patsubst $(n):%,%,$(filter $(n):%,$(pathmap_INCL)))) endef # # A list of all source roots under frameworks/base, which will be # built into the android.jar. # FRAMEWORKS_BASE_SUBDIRS := \ $(addsuffix /java, \ core \ graphics \ location \ media \ media/mca/effect \ media/mca/filterfw \ media/mca/filterpacks \ drm \ opengl \ sax \ telecomm \ telephony \ wifi \ lowpan \ keystore \ rs \ ) # # A version of FRAMEWORKS_BASE_SUBDIRS that is expanded to full paths from # the root of the tree. This currently needs to be here so that other libraries # and apps can find the .aidl files in the framework, though we should really # figure out a better way to do this. # FRAMEWORKS_BASE_JAVA_SRC_DIRS := \ $(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS)) ================================================ FILE: core/phony_package.mk ================================================ $(call record-module-type,PHONY_PACKAGE) ifneq ($(strip $(LOCAL_SRC_FILES)),) $(error LOCAL_SRC_FILES are not allowed for phony packages) endif LOCAL_MODULE_CLASS := FAKE LOCAL_MODULE_SUFFIX := -timestamp include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) $(hide) echo "Fake: $@" $(hide) mkdir -p $(dir $@) $(hide) touch $@ $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PHONY_PACKAGE)) ================================================ FILE: core/prebuilt.mk ================================================ ########################################################### ## Standard rules for copying files that are prebuilt ## ## Additional inputs from base_rules.make: ## None. ## ########################################################### $(call record-module-type,PREBUILT) ifdef LOCAL_IS_HOST_MODULE my_prefix := HOST_ LOCAL_HOST_PREFIX := else my_prefix := TARGET_ endif include $(BUILD_SYSTEM)/multilib.mk my_skip_non_preferred_arch := # check if first arch is supported LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # first arch is supported include $(BUILD_SYSTEM)/prebuilt_internal.mk ifneq ($(my_module_multilib),both) my_skip_non_preferred_arch := true endif # $(my_module_multilib) # For apps, we don't want to set up the prebuilt apk rule twice even if "LOCAL_MULTILIB := both". ifeq (APPS,$(LOCAL_MODULE_CLASS)) my_skip_non_preferred_arch := true endif endif # $(my_module_arch_supported) ifndef my_skip_non_preferred_arch ifneq (,$($(my_prefix)2ND_ARCH)) # check if secondary arch is supported LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # secondary arch is supported LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/prebuilt_internal.mk endif # $(my_module_arch_supported) endif # $($(my_prefix)2ND_ARCH) endif # $(my_skip_non_preferred_arch) not true LOCAL_2ND_ARCH_VAR_PREFIX := my_module_arch_supported := ================================================ FILE: core/prebuilt_internal.mk ================================================ ########################################################### ## Standard rules for copying files that are prebuilt ## ## Additional inputs from base_rules.make: ## None. ## ########################################################### include $(BUILD_SYSTEM)/use_lld_setup.mk ifneq ($(LOCAL_PREBUILT_LIBS),) $(call pretty-error,dont use LOCAL_PREBUILT_LIBS anymore) endif ifneq ($(LOCAL_PREBUILT_EXECUTABLES),) $(call pretty-error,dont use LOCAL_PREBUILT_EXECUTABLES anymore) endif ifneq ($(LOCAL_PREBUILT_JAVA_LIBRARIES),) $(call pretty-error,dont use LOCAL_PREBUILT_JAVA_LIBRARIES anymore) endif my_32_64_bit_suffix := $(if $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)IS_64_BIT),64,32) ifdef LOCAL_PREBUILT_MODULE_FILE my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE) else ifdef LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) LOCAL_SRC_FILES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH) := else ifdef LOCAL_SRC_FILES_$(my_32_64_bit_suffix) my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES_$(my_32_64_bit_suffix))) LOCAL_SRC_FILES_$(my_32_64_bit_suffix) := else ifdef LOCAL_SRC_FILES my_prebuilt_src_file := $(call clean-path,$(LOCAL_PATH)/$(LOCAL_SRC_FILES)) LOCAL_SRC_FILES := else ifdef LOCAL_REPLACE_PREBUILT_APK_INSTALLED # This is handled specially in app_prebuilt_internal.mk else $(call pretty-error,No source files specified) endif LOCAL_CHECKED_MODULE := $(my_prebuilt_src_file) ifneq (,$(LOCAL_APKCERTS_FILE)) PACKAGES := $(PACKAGES) $(LOCAL_MODULE) PACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE) endif ifneq (APPS,$(LOCAL_MODULE_CLASS)) ifdef LOCAL_COMPRESSED_MODULE $(error $(LOCAL_MODULE) : LOCAL_COMPRESSED_MODULE can only be defined for module class APPS) endif # LOCAL_COMPRESSED_MODULE endif # APPS ifeq (APPS,$(LOCAL_MODULE_CLASS)) include $(BUILD_SYSTEM)/app_prebuilt_internal.mk else ifeq (JAVA_LIBRARIES,$(LOCAL_MODULE_CLASS)) include $(BUILD_SYSTEM)/java_prebuilt_internal.mk else ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) include $(BUILD_SYSTEM)/cc_prebuilt_internal.mk else ifneq ($(filter SCRIPT ETC DATA RENDERSCRIPT_BITCODE,$(LOCAL_MODULE_CLASS)),) include $(BUILD_SYSTEM)/misc_prebuilt_internal.mk else $(error $(LOCAL_MODULE) : unexpected LOCAL_MODULE_CLASS for prebuilts: $(LOCAL_MODULE_CLASS)) endif $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)), \ $(eval ALL_MODULES.$(my_register_name).IS_PREBUILT_MAKE_MODULE := Y)) $(built_module) : $(LOCAL_ADDITIONAL_DEPENDENCIES) my_prebuilt_src_file := $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=PREBUILT)) ================================================ FILE: core/process_wrapper.sh ================================================ #!/bin/sh # When using a process wrapper, this is the top-level # command that is executed instead of the server # command. It starts a new xterm in which the user can # interact with the new process. # # Inside of the xterm is a gdb session, through which # the user can debug the new process. # Save away these variables, since we may loose them # when starting in the xterm. export PREV_LD_LIBRARY_PATH=$LD_LIBRARY_PATH export PREV_PATH=$PATH gnome-terminal -t "Wrapper: $1" --disable-factory -x $2/process_wrapper_gdb.sh "$@" ================================================ FILE: core/process_wrapper_gdb.cmds ================================================ run ================================================ FILE: core/process_wrapper_gdb.sh ================================================ #!/bin/sh # This is the command running inside the xterm of our # debug wrapper. It needs to take care of starting # the server command, so it can attach to the parent # process. In addition, here we run the command inside # of a gdb session to allow for debugging. # On some systems, running xterm will cause LD_LIBRARY_PATH # to be cleared, so restore it and PATH to be safe. export PATH=$PREV_PATH export LD_LIBRARY_PATH=$PREV_LD_LIBRARY_PATH # Start binderproc (or whatever sub-command is being run) # inside of gdb, giving gdb an initial command script to # automatically run the process without user intervention. gdb -q -x $2/process_wrapper_gdb.cmds --args "$@" ================================================ FILE: core/product-graph.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # the sort also acts as a strip to remove the single space entries that creep in because of the evals define gather-all-makefiles-for-current-product $(eval _all_products_visited := )\ $(sort $(call gather-all-makefiles-for-current-product-inner,$(INTERNAL_PRODUCT))) endef define gather-all-makefiles-for-current-product-inner $(foreach p,$(1),\ $(if $(filter $(p),$(_all_products_visited)),, \ $(p) \ $(eval _all_products_visited += $(p)) \ $(call gather-all-makefiles-for-current-product-inner, $(PRODUCTS.$(strip $(p)).INHERITS_FROM)) ) \ ) endef node_color_target := orange node_color_common := beige node_color_vendor := lavenderblush node_color_default := white define node-color $(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),\ $(node_color_target),\ $(if $(filter build/make/target/product/%,$(1)),\ $(node_color_common),\ $(if $(filter vendor/%,$(1)),$(node_color_vendor),$(node_color_default))\ )\ ) endef open_parethesis := ( close_parenthesis := ) # Emit properties of a product node to a file. # $(1) the product # $(2) the output file define emit-product-node-props $(hide) echo \"$(1)\" [ \ label=\"$(dir $(1))\\n$(notdir $(1))$(if $(filter $(1),$(PRIVATE_TOP_LEVEL_MAKEFILE)),$(subst $(open_parethesis),,$(subst $(close_parenthesis),,\\n\\n$(PRODUCT_MODEL)\\n$(PRODUCT_DEVICE))))\" \ style=\"filled\" fillcolor=\"$(strip $(call node-color,$(1)))\" \ colorscheme=\"svg\" fontcolor=\"darkblue\" \ ] >> $(2) endef products_graph := $(OUT_DIR)/products.dot $(products_graph): PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT := $(call gather-all-makefiles-for-current-product) $(products_graph): PRIVATE_TOP_LEVEL_MAKEFILE := $(INTERNAL_PRODUCT) $(products_graph): @echo Product graph DOT: $@ for $(PRIVATE_TOP_LEVEL_MAKEFILE) $(hide) echo 'digraph {' > $@ $(hide) echo 'graph [ ratio=.5 ];' >> $@ $(hide) $(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT), \ $(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), echo \"$(d)\" -\> \"$(p)\" >> $@;)) $(foreach p,$(PRIVATE_ALL_MAKEFILES_FOR_THIS_PRODUCT),$(call emit-product-node-props,$(p),$@)) $(hide) echo '}' >> $@ .PHONY: product-graph product-graph: $(products_graph) @echo Product graph .dot file: $(products_graph) @echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph) @echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph) ================================================ FILE: core/product.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Variables that are meant to hold only a single value. # - The value set in the current makefile takes precedence over inherited values # - If multiple inherited makefiles set the var, the first-inherited value wins _product_single_value_vars := # Variables that are lists of values. _product_list_vars := _product_single_value_vars += PRODUCT_NAME _product_single_value_vars += PRODUCT_MODEL _product_single_value_vars += PRODUCT_NAME_FOR_ATTESTATION _product_single_value_vars += PRODUCT_MODEL_FOR_ATTESTATION _product_single_value_vars += PRODUCT_BASE_OS # Defines the ELF segment alignment for binaries (executables and shared libraries). # The ELF segment alignment has to be a PAGE_SIZE multiple. For example, if # PRODUCT_MAX_PAGE_SIZE_SUPPORTED=65536, the possible values for PAGE_SIZE could be # 4096, 16384 and 65536. _product_single_value_vars += PRODUCT_MAX_PAGE_SIZE_SUPPORTED _product_single_value_vars += PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE # Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro. _product_single_value_vars += PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO # The resource configuration options to use for this product. _product_list_vars += PRODUCT_LOCALES _product_list_vars += PRODUCT_AAPT_CONFIG _product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG _product_list_vars += PRODUCT_AAPT_PREBUILT_DPI _product_list_vars += PRODUCT_HOST_PACKAGES _product_list_vars += PRODUCT_PACKAGES _product_list_vars += PRODUCT_PACKAGES_DEBUG _product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN _product_list_vars += PRODUCT_PACKAGES_ARM64 # packages that are added to PRODUCT_PACKAGES based on the PRODUCT_SHIPPING_API_LEVEL # These are only added if the shipping API level is that level or lower _product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29 _product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33 _product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 # Packages included only for eng/userdebug builds, when building with EMMA_INSTRUMENT=true _product_list_vars += PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE _product_list_vars += PRODUCT_PACKAGES_ENG _product_list_vars += PRODUCT_PACKAGES_TESTS # The device that this product maps to. _product_single_value_vars += PRODUCT_DEVICE _product_single_value_vars += PRODUCT_MANUFACTURER _product_single_value_vars += PRODUCT_BRAND _product_single_value_vars += PRODUCT_BRAND_FOR_ATTESTATION # These PRODUCT_SYSTEM_* flags, if defined, are used in place of the # corresponding PRODUCT_* flags for the sysprops on /system. _product_single_value_vars += \ PRODUCT_SYSTEM_NAME \ PRODUCT_SYSTEM_MODEL \ PRODUCT_SYSTEM_DEVICE \ PRODUCT_SYSTEM_BRAND \ PRODUCT_SYSTEM_MANUFACTURER \ # PRODUCT__PROPERTIES are lists of property assignments # that go to /build.prop. Each property assignment is # "key = value" with zero or more whitespace characters on either # side of the '='. _product_list_vars += \ PRODUCT_SYSTEM_PROPERTIES \ PRODUCT_SYSTEM_EXT_PROPERTIES \ PRODUCT_VENDOR_PROPERTIES \ PRODUCT_ODM_PROPERTIES \ PRODUCT_PRODUCT_PROPERTIES # TODO(b/117892318) deprecate these: # ... in favor or PRODUCT_SYSTEM_PROPERTIES _product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES # ... in favor of PRODUCT_VENDOR_PROPERTIES _product_list_vars += PRODUCT_PROPERTY_OVERRIDES _product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES # TODO(b/117892318) consider deprecating these too _product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST _product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST # The characteristics of the product, which among other things is passed to aapt _product_single_value_vars += PRODUCT_CHARACTERISTICS # A list of words like :[:]. # The file at the source path should be copied to the destination path # when building this product. is relative to # $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml". # The rules for these copy steps are defined in build/make/core/Makefile. # The optional : is used to indicate the owner of a vendor file. _product_list_vars += PRODUCT_COPY_FILES # The OTA key(s) specified by the product config, if any. The names # of these keys are stored in the target-files zip so that post-build # signing tools can substitute them for the test key embedded by # default. _product_list_vars += PRODUCT_OTA_PUBLIC_KEYS _product_list_vars += PRODUCT_EXTRA_OTA_KEYS _product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS # Should we use the default resources or add any product specific overlays _product_list_vars += PRODUCT_PACKAGE_OVERLAYS _product_list_vars += DEVICE_PACKAGE_OVERLAYS # Resource overlay list which must be excluded from enforcing RRO. _product_list_vars += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS # Package list to apply enforcing RRO. _product_list_vars += PRODUCT_ENFORCE_RRO_TARGETS _product_list_vars += PRODUCT_SDK_ATREE_FILES _product_list_vars += PRODUCT_SDK_ADDON_NAME _product_list_vars += PRODUCT_SDK_ADDON_COPY_FILES _product_list_vars += PRODUCT_SDK_ADDON_COPY_MODULES _product_list_vars += PRODUCT_SDK_ADDON_DOC_MODULES _product_list_vars += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP # which Soong namespaces to export to Make _product_list_vars += PRODUCT_SOONG_NAMESPACES _product_list_vars += PRODUCT_DEFAULT_WIFI_CHANNELS _product_single_value_vars += PRODUCT_DEFAULT_DEV_CERTIFICATE _product_list_vars += PRODUCT_MAINLINE_SEPOLICY_DEV_CERTIFICATES _product_list_vars += PRODUCT_RESTRICT_VENDOR_FILES # The list of product-specific kernel header dirs _product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS # A list of module names in BOOTCLASSPATH (jar files). Each module may be # prefixed with ":", which identifies the APEX that provides it. APEXes # are identified by their "variant" names, i.e. their `apex_name` values in # Soong, which default to the `name` values. The prefix can also be "platform:" # or "system_ext:", and defaults to "platform:" if left out. See the long # comment in build/soong/java/dexprepopt_bootjars.go for details. _product_list_vars += PRODUCT_BOOT_JARS # A list of extra BOOTCLASSPATH jars (to be appended after common jars), # following the same format as PRODUCT_BOOT_JARS. Products that include # device-specific makefiles before AOSP makefiles should use this instead of # PRODUCT_BOOT_JARS, so that device-specific jars go after common jars. _product_list_vars += PRODUCT_BOOT_JARS_EXTRA # List of jars to be included in the ART boot image for testing. _product_list_vars += PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS _product_list_vars += PRODUCT_SYSTEM_SERVER_APPS # List of system_server classpath jars on the platform. _product_list_vars += PRODUCT_SYSTEM_SERVER_JARS # List of system_server classpath jars delivered via apex. Format = :. _product_list_vars += PRODUCT_APEX_SYSTEM_SERVER_JARS # List of jars on the platform that system_server loads dynamically using separate classloaders. _product_list_vars += PRODUCT_STANDALONE_SYSTEM_SERVER_JARS # List of jars delivered via apex that system_server loads dynamically using separate classloaders. # Format = : _product_list_vars += PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS # If true, then suboptimal order of system server jars does not cause an error. _product_single_value_vars += PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS # If true, then system server jars defined in Android.mk are supported. _product_single_value_vars += PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS # Additional system server jars to be appended at the end of the common list. # This is necessary to avoid jars reordering due to makefile inheritance order. _product_list_vars += PRODUCT_SYSTEM_SERVER_JARS_EXTRA # Set to true to disable checks for a product. _product_list_vars += PRODUCT_BROKEN_VERIFY_USES_LIBRARIES _product_list_vars += PRODUCT_DEXPREOPT_SPEED_APPS _product_list_vars += PRODUCT_LOADED_BY_PRIVILEGED_MODULES _product_single_value_vars += PRODUCT_VBOOT_SIGNING_KEY _product_single_value_vars += PRODUCT_VBOOT_SIGNING_SUBKEY _product_single_value_vars += PRODUCT_SYSTEM_VERITY_PARTITION _product_single_value_vars += PRODUCT_VENDOR_VERITY_PARTITION _product_single_value_vars += PRODUCT_PRODUCT_VERITY_PARTITION _product_single_value_vars += PRODUCT_SYSTEM_EXT_VERITY_PARTITION _product_single_value_vars += PRODUCT_ODM_VERITY_PARTITION _product_single_value_vars += PRODUCT_VENDOR_DLKM_VERITY_PARTITION _product_single_value_vars += PRODUCT_ODM_DLKM_VERITY_PARTITION _product_single_value_vars += PRODUCT_SYSTEM_DLKM_VERITY_PARTITION _product_single_value_vars += PRODUCT_SYSTEM_SERVER_DEBUG_INFO _product_single_value_vars += PRODUCT_OTHER_JAVA_DEBUG_INFO # Per-module dex-preopt configs. _product_list_vars += PRODUCT_DEX_PREOPT_MODULE_CONFIGS _product_single_value_vars += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER _product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS _product_single_value_vars += PRODUCT_DEX_PREOPT_BOOT_FLAGS _product_single_value_vars += PRODUCT_DEX_PREOPT_PROFILE_DIR _product_single_value_vars += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES _product_single_value_vars += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING _product_single_value_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS # Boot image options. _product_list_vars += PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION _product_single_value_vars += \ PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST \ PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \ PRODUCT_USES_DEFAULT_ART_CONFIG \ _product_single_value_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER # Per-module sanitizer configs _product_list_vars += PRODUCT_SANITIZER_MODULE_CONFIGS _product_single_value_vars += PRODUCT_SYSTEM_BASE_FS_PATH _product_single_value_vars += PRODUCT_VENDOR_BASE_FS_PATH _product_single_value_vars += PRODUCT_PRODUCT_BASE_FS_PATH _product_single_value_vars += PRODUCT_SYSTEM_EXT_BASE_FS_PATH _product_single_value_vars += PRODUCT_ODM_BASE_FS_PATH _product_single_value_vars += PRODUCT_VENDOR_DLKM_BASE_FS_PATH _product_single_value_vars += PRODUCT_ODM_DLKM_BASE_FS_PATH _product_single_value_vars += PRODUCT_SYSTEM_DLKM_BASE_FS_PATH # The first API level this product shipped with _product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL _product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES _product_list_vars += VENDOR_EXCEPTION_MODULES _product_list_vars += VENDOR_EXCEPTION_PATHS # Whether the product wants to ship libartd. For rules and meaning, see art/Android.mk. _product_single_value_vars += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD # Make this art variable visible to soong_config.mk. _product_single_value_vars += PRODUCT_ART_USE_READ_BARRIER # Add reserved headroom to a system image. _product_single_value_vars += PRODUCT_SYSTEM_HEADROOM # Whether to save disk space by minimizing java debug info _product_single_value_vars += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO # Whether any paths are excluded from sanitization when SANITIZE_TARGET=integer_overflow _product_list_vars += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS _product_single_value_vars += PRODUCT_ADB_KEYS # Whether any paths should have CFI enabled for components _product_list_vars += PRODUCT_CFI_INCLUDE_PATHS # Whether any paths are excluded from sanitization when SANITIZE_TARGET=cfi _product_list_vars += PRODUCT_CFI_EXCLUDE_PATHS # Whether any paths should have HWASan enabled for components _product_list_vars += PRODUCT_HWASAN_INCLUDE_PATHS # Whether any paths are excluded from sanitization when SANITIZE_TARGET=hwaddress _product_list_vars += PRODUCT_HWASAN_EXCLUDE_PATHS # Whether any paths should have Memtag_heap enabled for components _product_list_vars += PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS _product_list_vars += PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS _product_list_vars += PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS _product_list_vars += PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS _product_list_vars += PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS # Whether this product wants to start with an empty list of default memtag_heap include paths _product_single_value_vars += PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS # Whether the Scudo hardened allocator is disabled platform-wide _product_single_value_vars += PRODUCT_DISABLE_SCUDO # List of extra VNDK versions to be included _product_list_vars += PRODUCT_EXTRA_VNDK_VERSIONS # Whether APEX should be compressed or not _product_single_value_vars += PRODUCT_COMPRESSED_APEX # Default fs type for APEX payload image (apex_payload.img) _product_single_value_vars += PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE # VNDK version of product partition. It can be 'current' if the product # partitions uses PLATFORM_VNDK_VERSION. _product_single_value_vars += PRODUCT_PRODUCT_VNDK_VERSION _product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS _product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT _product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST _product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT _product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST # List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence # installed on /system directory by default. _product_list_vars += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION # When this is true, dynamic partitions is retrofitted on a device that has # already been launched without dynamic partitions. Otherwise, the device # is launched with dynamic partitions. # This flag implies PRODUCT_USE_DYNAMIC_PARTITIONS. _product_single_value_vars += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS # List of directories that will be used to gate blueprint modules from the build graph _product_list_vars += PRODUCT_SOURCE_ROOT_DIRS # When this is true, various build time as well as runtime debugfs restrictions are enabled. _product_single_value_vars += PRODUCT_SET_DEBUGFS_RESTRICTIONS # Other dynamic partition feature flags.PRODUCT_USE_DYNAMIC_PARTITION_SIZE and # PRODUCT_BUILD_SUPER_PARTITION default to the value of PRODUCT_USE_DYNAMIC_PARTITIONS. _product_single_value_vars += \ PRODUCT_USE_DYNAMIC_PARTITIONS \ PRODUCT_USE_DYNAMIC_PARTITION_SIZE \ PRODUCT_BUILD_SUPER_PARTITION \ # If set, kernel configuration requirements are present in OTA package (and will be enforced # during OTA). Otherwise, kernel configuration requirements are enforced in VTS. # Devices that checks the running kernel (instead of the kernel in OTA package) should not # set this variable to prevent OTA failures. _product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS # If set to true, this product forces HIDL to be enabled by declaring android.hidl.manager # and android.hidl.token in the framework manifest. The product will also need to add the # 'hwservicemanager' service to PRODUCT_PACKAGES if its SHIPPING_API_LEVEL is greater than 34. # This should only be used during bringup for devices that are targeting FCM 202404 and still # have partner-owned HIDL interfaces that are being converted to AIDL. _product_single_value_vars += PRODUCT_HIDL_ENABLED # If set to true, this product builds a generic OTA package, which installs generic system images # onto matching devices. The product may only build a subset of system images (e.g. only # system.img), so devices need to install the package in a system-only OTA manner. _product_single_value_vars += PRODUCT_BUILD_GENERIC_OTA_PACKAGE _product_list_vars += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES _product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES _product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES # Overrides the (apex, jar) pairs above when determining the on-device location. The format is: # ::: _product_list_vars += PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES # Controls for whether different partitions are built for the current product. _product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE _product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE _product_single_value_vars += PRODUCT_BUILD_VENDOR_IMAGE _product_single_value_vars += PRODUCT_BUILD_PRODUCT_IMAGE _product_single_value_vars += PRODUCT_BUILD_SYSTEM_EXT_IMAGE _product_single_value_vars += PRODUCT_BUILD_ODM_IMAGE _product_single_value_vars += PRODUCT_BUILD_VENDOR_DLKM_IMAGE _product_single_value_vars += PRODUCT_BUILD_ODM_DLKM_IMAGE _product_single_value_vars += PRODUCT_BUILD_SYSTEM_DLKM_IMAGE _product_single_value_vars += PRODUCT_BUILD_CACHE_IMAGE _product_single_value_vars += PRODUCT_BUILD_RAMDISK_IMAGE _product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE _product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE _product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_INIT_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_VENDOR_KERNEL_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE _product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE _product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE _product_single_value_vars += PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE _product_single_value_vars += PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE # List of boot jars delivered via updatable APEXes, following the same format as # PRODUCT_BOOT_JARS. _product_list_vars += PRODUCT_APEX_BOOT_JARS # If set, device uses virtual A/B. _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA # If set, device uses virtual A/B Compression. _product_single_value_vars += PRODUCT_VIRTUAL_AB_COMPRESSION # If set, device retrofits virtual A/B. _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA_RETROFIT # If set, forcefully generate a non-A/B update package. # Note: A device configuration should inherit from virtual_ab_ota_plus_non_ab.mk # instead of setting this variable directly. # Note: Use TARGET_OTA_ALLOW_NON_AB in the build system because # TARGET_OTA_ALLOW_NON_AB takes the value of AB_OTA_UPDATER into account. _product_single_value_vars += PRODUCT_OTA_FORCE_NON_AB_PACKAGE # If set, Java module in product partition cannot use hidden APIs. _product_single_value_vars += PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE # Install a copy of the debug policy to the system_ext partition, and allow # init-second-stage to load debug policy from system_ext. # This option is only meant to be set by compliance GSI targets. _product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT # If set, fsverity metadata files will be generated for each files in the # allowlist, plus an manifest APK per partition. For example, # /system/framework/service.jar will come with service.jar.fsv_meta in the same # directory; the file information will also be included in # /system/etc/security/fsverity/BuildManifest.apk _product_single_value_vars += PRODUCT_FSVERITY_GENERATE_METADATA # If true, this builds the mainline modules from source. This overrides any # prebuilts selected via RELEASE_APEX_CONTRIBUTIONS_* build flags for the # current release config. _product_single_value_vars += PRODUCT_MODULE_BUILD_FROM_SOURCE # If true, installs a full version of com.android.virt APEX. _product_single_value_vars += PRODUCT_AVF_ENABLED # If false, disable the AVF remote attestaton feature. _product_single_value_vars += PRODUCT_AVF_REMOTE_ATTESTATION_DISABLED # If true, kernel with modules will be used for Microdroid VMs. _product_single_value_vars += PRODUCT_AVF_KERNEL_MODULES_ENABLED # If true, the memory controller will be force-enabled in the cgroup v2 hierarchy _product_single_value_vars += PRODUCT_MEMCG_V2_FORCE_ENABLED # If true, the cgroup v2 hierarchy will be split into apps/system subtrees _product_single_value_vars += PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED # List of .json files to be merged/compiled into vendor/etc/linker.config.pb and product/etc/linker.config.pb _product_list_vars += PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS _product_list_vars += PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS # Whether to use userfaultfd GC. # Possible values are: # - "default" or empty: both the build system and the runtime determine whether to use userfaultfd # GC based on the vendor API level # - "true": forces the build system to use userfaultfd GC regardless of the vendor API level; the # runtime determines whether to use userfaultfd GC based on the kernel support. Note that the # device may have to re-compile everything on the first boot if the kernel doesn't support # userfaultfd # - "false": disallows the build system and the runtime to use userfaultfd GC even if the device # supports it. This option is temporary - the plan is to remove it by Aug 2025, at which time # Mainline updates of the ART module will ignore it as well. _product_single_value_vars += PRODUCT_ENABLE_UFFD_GC # Specifies COW version to be used by update_engine and libsnapshot. If this value is not # specified we default to COW version 2 in update_engine for backwards compatibility _product_single_value_vars += PRODUCT_VIRTUAL_AB_COW_VERSION # Specifies maximum bytes to be compressed at once during ota. Options: 4096, 8192, 16384, 32768, 65536, 131072, 262144. _product_single_value_vars += PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR # If set, determines whether the build system checks vendor seapp contexts violations. _product_single_value_vars += PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS # If set, determines whether the build system checks dev type violations. _product_single_value_vars += PRODUCT_CHECK_DEV_TYPE_VIOLATIONS _product_list_vars += PRODUCT_AFDO_PROFILES _product_single_value_vars += PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE _product_list_vars += PRODUCT_RELEASE_CONFIG_MAPS _product_list_vars += PRODUCT_VALIDATION_CHECKS _product_single_value_vars += PRODUCT_BUILD_FROM_SOURCE_STUB _product_single_value_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS _product_single_value_vars += PRODUCT_HIDDEN_API_EXPORTABLE_STUBS _product_single_value_vars += PRODUCT_EXPORT_RUNTIME_APIS # If set, determines which version of the GKI is used as guest kernel for Microdroid VMs. # TODO(b/325991735): link to documentation once it is done. _product_single_value_vars += PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION # Enables 16KB developer option for device if set. _product_single_value_vars += PRODUCT_16K_DEVELOPER_OPTION # If set, adb root will be disabled (really ro.debuggable=0) in userdebug # builds. It's already off disabled in user builds. Eng builds are unaffected # by this flag. _product_single_value_vars += PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG # If set, the default value of the versionName of apps will include the build number. _product_single_value_vars += PRODUCT_BUILD_APPS_WITH_BUILD_NUMBER # If set, build would generate system image from Soong-defined module. _product_single_value_vars += PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE # List of stub libraries specific to the product that are already present in the system image and # should be included in the system_linker_config. _product_list_vars += PRODUCT_EXTRA_STUB_LIBRARIES # If set to true, all Android.mk files will be ignored. _product_single_value_vars += PRODUCT_IGNORE_ALL_ANDROIDMK # When PRODUCT_IGNORE_ALL_ANDROIDMK is set to true, this variable will be used to allow some Android.mk files. _product_list_vars += PRODUCT_ALLOWED_ANDROIDMK_FILES # When PRODUCT_IGNORE_ALL_ANDROIDMK is set to true, path of file that contains a list of allowed Android.mk files _product_single_value_vars += PRODUCT_ANDROIDMK_ALLOWLIST_FILE # Setting PRODUCT_SOONG_ONLY will cause the build to default to --soong-only mode, and the main # kati invocation will not be run. _product_single_value_vars += PRODUCT_SOONG_ONLY # If set to true, use NOTICE.xml.gz generated by soong _product_single_value_vars += PRODUCT_USE_SOONG_NOTICE_XML .KATI_READONLY := _product_single_value_vars _product_list_vars _product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars) # # Functions for including product makefiles # # # $(1): product to inherit # # To be called from product makefiles, and is later evaluated during the import-nodes # call below. It does the following: # 1. Inherits all of the variables from $1. # 2. Records the inheritance in the .INHERITS_FROM variable # # (2) and the PRODUCTS variable can be used together to reconstruct the include hierarchy # See e.g. product-graph.mk for an example of this. # define inherit-product $(eval _inherit_product_wildcard := $(wildcard $(1)))\ $(if $(_inherit_product_wildcard),,$(error $(1) does not exist.))\ $(foreach part,$(_inherit_product_wildcard),\ $(if $(findstring ../,$(part)),\ $(eval np := $(call normalize-paths,$(part))),\ $(eval np := $(strip $(part))))\ $(foreach v,$(_product_var_list), \ $(eval $(v) := $($(v)) $(INHERIT_TAG)$(np))) \ $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ $(eval inherit_var := PRODUCTS.$(current_mk).INHERITS_FROM) \ $(eval $(inherit_var) := $(sort $($(inherit_var)) $(np))) \ $(call dump-inherit,$(current_mk),$(1)) \ $(call dump-config-vals,$(current_mk),inherit)) endef # Specifies a number of path prefixes, relative to PRODUCT_OUT, where the # product makefile hierarchy rooted in the current node places its artifacts. # Creating artifacts outside the specified paths will cause a build-time error. define require-artifacts-in-path $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENTS := $(strip $(1))) \ $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_ALLOWED_LIST := $(strip $(2))) \ $(eval ARTIFACT_PATH_REQUIREMENT_PRODUCTS := \ $(sort $(ARTIFACT_PATH_REQUIREMENT_PRODUCTS) $(current_mk))) endef # Like require-artifacts-in-path, but does not require all allow-list entries to # have an effect. define require-artifacts-in-path-relaxed $(require-artifacts-in-path) \ $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENT_IS_RELAXED := true) endef # Makes including non-existent modules in PRODUCT_PACKAGES an error. # $(1): list of non-existent modules to allow. define enforce-product-packages-exist $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \ $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST := true) \ $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST := $(1)) \ $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST) \ $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST) endef # # Do inherit-product only if $(1) exists # define inherit-product-if-exists $(if $(wildcard $(1)),$(call inherit-product,$(1)),) endef # # $(1): product makefile list # #TODO: check to make sure that products have all the necessary vars defined define import-products $(call import-nodes,PRODUCTS,$(1),$(_product_var_list),$(_product_single_value_vars)) endef # # Does various consistency checks on the current product. # Takes no parameters, so $(call ) is not necessary. # define check-current-product $(if ,, \ $(if $(call is-c-identifier,$(PRODUCT_NAME)),, \ $(error $(INTERNAL_PRODUCT): PRODUCT_NAME must be a valid C identifier, not "$(pn)")) \ $(if $(PRODUCT_BRAND),, \ $(error $(INTERNAL_PRODUCT): PRODUCT_BRAND must be defined.)) \ $(foreach cf,$(strip $(PRODUCT_COPY_FILES)), \ $(if $(filter 2 3,$(words $(subst :,$(space),$(cf)))),, \ $(error $(p): malformed COPY_FILE "$(cf)")))) endef # BoardConfig variables that are also inherited in product mks. Should ideally # be cleaned up to not be product variables. _readonly_late_variables := \ DEVICE_PACKAGE_OVERLAYS \ WITH_DEXPREOPT_ART_BOOT_IMG_ONLY \ # Modified internally in the build system _readonly_late_variables += \ PRODUCT_COPY_FILES \ PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING \ PRODUCT_DEX_PREOPT_BOOT_FLAGS \ _readonly_early_variables := $(filter-out $(_readonly_late_variables),$(_product_var_list)) # # Mark the variables in _product_stash_var_list as readonly # define readonly-variables $(foreach v,$(1), \ $(eval $(v) ?=) \ $(eval .KATI_READONLY := $(v)) \ ) endef define readonly-product-vars $(call readonly-variables,$(_readonly_early_variables)) endef define readonly-final-product-vars $(call readonly-variables,$(_readonly_late_variables)) endef # Macro re-defined inside strip-product-vars. get-product-var = $(PRODUCTS.$(strip $(1)).$(2)) # # Strip the variables in _product_var_list and a few build-system # internal variables, and assign the ones for the current product # to a shorthand that is more convenient to read from elsewhere. # define strip-product-vars $(call dump-phase-start,PRODUCT-EXPAND,,$(_product_var_list),$(_product_single_value_vars), \ build/make/core/product.mk) \ $(foreach v,\ $(_product_var_list) \ PRODUCT_ENFORCE_PACKAGES_EXIST \ PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST, \ $(eval $(v) := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).$(v)))) \ $(eval get-product-var = $$(if $$(filter $$(1),$$(INTERNAL_PRODUCT)),$$($$(2)),$$(PRODUCTS.$$(strip $$(1)).$$(2)))) \ $(KATI_obsolete_var PRODUCTS.$(INTERNAL_PRODUCT).$(v),Use $(v) instead) \ ) \ $(call dump-phase-end,build/make/core/product.mk) endef define add-to-product-copy-files-if-exists $(if $(wildcard $(word 1,$(subst :, ,$(1)))),$(1)) endef # whitespace placeholder when we record module's dex-preopt config. _PDPMC_SP_PLACE_HOLDER := |@SP@| # Set up dex-preopt config for a module. # $(1) list of module names # $(2) the modules' dex-preopt config define add-product-dex-preopt-module-config $(eval _c := $(subst $(space),$(_PDPMC_SP_PLACE_HOLDER),$(strip $(2))))\ $(eval PRODUCT_DEX_PREOPT_MODULE_CONFIGS += \ $(foreach m,$(1),$(m)=$(_c))) endef # whitespace placeholder when we record module's sanitizer config. _PSMC_SP_PLACE_HOLDER := |@SP@| # Set up sanitizer config for a module. # $(1) list of module names # $(2) the modules' sanitizer config define add-product-sanitizer-module-config $(eval _c := $(subst $(space),$(_PSMC_SP_PLACE_HOLDER),$(strip $(2))))\ $(eval PRODUCT_SANITIZER_MODULE_CONFIGS += \ $(foreach m,$(1),$(m)=$(_c))) endef ================================================ FILE: core/product_config.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # --------------------------------------------------------------- # Generic functions # TODO: Move these to definitions.make once we're able to include # definitions.make before config.make. ########################################################### ## Return non-empty if $(1) is a C identifier; i.e., if it ## matches /^[a-zA-Z_][a-zA-Z0-9_]*$/. We do this by first ## making sure that it isn't empty and doesn't start with ## a digit, then by removing each valid character. If the ## final result is empty, then it was a valid C identifier. ## ## $(1): word to check ########################################################### _ici_digits := 0 1 2 3 4 5 6 7 8 9 _ici_alphaunderscore := \ a b c d e f g h i j k l m n o p q r s t u v w x y z \ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ define is-c-identifier $(strip \ $(if $(1), \ $(if $(filter $(addsuffix %,$(_ici_digits)),$(1)), \ , \ $(eval w := $(1)) \ $(foreach c,$(_ici_digits) $(_ici_alphaunderscore), \ $(eval w := $(subst $(c),,$(w))) \ ) \ $(if $(w),,TRUE) \ $(eval w :=) \ ) \ ) \ ) endef # TODO: push this into the combo files; unfortunately, we don't even # know HOST_OS at this point. trysed := $(shell echo a | sed -E -e 's/a/b/' 2>/dev/null) ifeq ($(trysed),b) SED_EXTENDED := sed -E else trysed := $(shell echo c | sed -r -e 's/c/d/' 2>/dev/null) ifeq ($(trysed),d) SED_EXTENDED := sed -r else $(error Unknown sed version) endif endif ########################################################### ## List all of the files in a subdirectory in a format ## suitable for PRODUCT_COPY_FILES and ## PRODUCT_SDK_ADDON_COPY_FILES ## ## $(1): Glob to match file name ## $(2): Source directory ## $(3): Target base directory ########################################################### define find-copy-subdir-files $(shell find $(2) -name "$(1)" -type f | $(SED_EXTENDED) "s:($(2)/?(.*)):\\1\\:$(3)/\\2:" | sed "s://:/:g" | sort) endef # # Convert file file to the PRODUCT_COPY_FILES/PRODUCT_SDK_ADDON_COPY_FILES # format: for each file F return $(F):$(PREFIX)/$(notdir $(F)) # $(1): files list # $(2): prefix define copy-files $(foreach f,$(1),$(f):$(2)/$(notdir $(f))) endef # # Convert the list of file names to the list of PRODUCT_COPY_FILES items # $(1): from pattern # $(2): to pattern # $(3): file names # E.g., calling product-copy-files-by-pattern with # (from/%, to/%, a b) # returns # from/a:to/a from/b:to/b define product-copy-files-by-pattern $(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3))) endef # Return empty unless the board matches define is-board-platform2 $(filter $(1), $(TARGET_BOARD_PLATFORM)) endef # Return empty unless the board is in the list define is-board-platform-in-list2 $(filter $(1),$(TARGET_BOARD_PLATFORM)) endef # Return empty unless the board is QCOM define is-vendor-board-qcom $(if $(strip $(TARGET_BOARD_PLATFORM) $(QCOM_BOARD_PLATFORMS)),$(filter $(TARGET_BOARD_PLATFORM),$(QCOM_BOARD_PLATFORMS)),\ $(error both TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) and QCOM_BOARD_PLATFORMS=$(QCOM_BOARD_PLATFORMS))) endef # --------------------------------------------------------------- # Check for obsolete PRODUCT- and APP- goals ifeq ($(CALLED_FROM_SETUP),true) product_goals := $(strip $(filter PRODUCT-%,$(MAKECMDGOALS))) ifdef product_goals $(error The PRODUCT-* goal is no longer supported. Use `TARGET_PRODUCT= m droid` instead) endif unbundled_goals := $(strip $(filter APP-%,$(MAKECMDGOALS))) ifdef unbundled_goals $(error The APP-* goal is no longer supported. Use `TARGET_BUILD_APPS="" m droid` instead) endif # unbundled_goals endif # Default to building dalvikvm on hosts that support it... ifeq ($(HOST_OS),linux) # ... or if the if the option is already set ifeq ($(WITH_HOST_DALVIK),) WITH_HOST_DALVIK := true endif endif # --------------------------------------------------------------- # Include the product definitions. # We need to do this to translate TARGET_PRODUCT into its # underlying TARGET_DEVICE before we start defining any rules. # include $(BUILD_SYSTEM)/node_fns.mk include $(BUILD_SYSTEM)/product.mk # Read all product definitions. # # Products are defined in AndroidProducts.mk files: android_products_makefiles := $(file <$(OUT_DIR)/.module_paths/AndroidProducts.mk.list) \ $(SRC_TARGET_DIR)/product/AndroidProducts.mk # An AndroidProduct.mk file sets the following variables: # PRODUCT_MAKEFILES specifies product makefiles. Each item in this list # is either a :path/to/file.mk, or just path/to/ # COMMON_LUNCH_CHOICES specifies - values to be shown # in the `lunch` menu # STARLARK_OPT_IN_PRODUCTS specifies products to use Starlark-based # product configuration by default # Builds a list of first/second elements of each pair: # $(call _first,a:A b:B,:) returns 'a b' # $(call _second,a-A b-B,-) returns 'A B' _first=$(filter-out $(2)%,$(subst $(2),$(space)$(2),$(1))) _second=$(filter-out %$(2),$(subst $(2),$(2)$(space),$(1))) # Returns : pair from a PRODUCT_MAKEFILE item. # If an item is :path/to/file.mk, return it as is, # otherwise assume that an item is path/to/.mk and # return :path/to/.mk _product-spec=$(strip $(if $(findstring :,$(1)),$(1),$(basename $(notdir $(1))):$(1))) # Reads given AndroidProduct.mk file and sets the following variables: # ap_product_paths -- the list of : pairs # ap_common_lunch_choices -- the list of - items # ap_products_using_starlark_config -- the list of products using starlark config # In addition, validates COMMON_LUNCH_CHOICES and STARLARK_OPT_IN_PRODUCTS values define _read-ap-file $(eval PRODUCT_MAKEFILES :=) \ $(eval COMMON_LUNCH_CHOICES :=) \ $(eval STARLARK_OPT_IN_PRODUCTS := ) \ $(eval ap_product_paths :=) \ $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \ $(eval include $(f)) \ $(foreach p, $(PRODUCT_MAKEFILES),$(eval ap_product_paths += $(call _product-spec,$(p)))) \ $(eval ap_common_lunch_choices := $(COMMON_LUNCH_CHOICES)) \ $(eval ap_products_using_starlark_config := $(STARLARK_OPT_IN_PRODUCTS)) \ $(eval _products := $(call _first,$(ap_product_paths),:)) \ $(eval _bad := $(filter-out $(_products),$(call _first,$(ap_common_lunch_choices),-))) \ $(if $(_bad),$(error COMMON_LUNCH_CHOICES contains products(s) not defined in this file: $(_bad))) \ $(eval _bad := $(filter-out %-eng %-userdebug %-user,$(ap_common_lunch_choices))) \ $(if $(_bad),$(error invalid variant in COMMON_LUNCH_CHOICES: $(_bad))) $(eval _bad := $(filter-out $(_products),$(ap_products_using_starlark_config))) \ $(if $(_bad),$(error STARLARK_OPT_IN_PRODUCTS contains product(s) not defined in this file: $(_bad))) endef # Build cumulative lists of all product specs/lunch choices/Starlark-based products. product_paths := common_lunch_choices := products_using_starlark_config := $(foreach f,$(android_products_makefiles), \ $(call _read-ap-file,$(f)) \ $(eval product_paths += $(ap_product_paths)) \ $(eval common_lunch_choices += $(ap_common_lunch_choices)) \ $(eval products_using_starlark_config += $(ap_products_using_starlark_config)) \ ) # Dedup, extract product names, etc. product_paths := $(sort $(product_paths)) all_named_products := $(sort $(call _first,$(product_paths),:)) current_product_makefile := $(call _second,$(filter $(TARGET_PRODUCT):%,$(product_paths)),:) COMMON_LUNCH_CHOICES := $(sort $(common_lunch_choices)) # Check that there are no duplicate product names $(foreach p,$(all_named_products), \ $(if $(filter 1,$(words $(filter $(p):%,$(product_paths)))),, \ $(error Product name must be unique, "$(p)" used by $(call _second,$(filter $(p):%,$(product_paths)),:)))) ifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),) _product_config_saved_KATI_ALLOW_RULES := $(.KATI_ALLOW_RULES) .KATI_ALLOW_RULES := $(ALLOW_RULES_IN_PRODUCT_CONFIG) endif ifeq (,$(current_product_makefile)) $(error Cannot locate config makefile for product "$(TARGET_PRODUCT)") endif ifneq (,$(filter $(TARGET_PRODUCT),$(products_using_starlark_config))) RBC_PRODUCT_CONFIG := true endif ifndef RBC_PRODUCT_CONFIG $(call import-products, $(current_product_makefile)) else $(shell mkdir -p $(OUT_DIR)/rbc) $(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_product_config.mk) $(shell $(OUT_DIR)/mk2rbc \ --mode=write -r --outdir $(OUT_DIR)/rbc \ --launcher=$(OUT_DIR)/rbc/launcher.rbc \ --input_variables=$(OUT_DIR)/rbc/make_vars_pre_product_config.mk \ --makefile_list=$(OUT_DIR)/.module_paths/configuration.list \ $(current_product_makefile)) ifneq ($(.SHELLSTATUS),0) $(error product configuration converter failed: $(.SHELLSTATUS)) endif $(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_product_config_results.mk \ $(OUT_DIR)/rbcrun --mode=rbc $(OUT_DIR)/rbc/launcher.rbc) ifneq ($(.SHELLSTATUS),0) $(error product configuration runner failed: $(.SHELLSTATUS)) endif include $(OUT_DIR)/rbc/rbc_product_config_results.mk endif # This step was already handled in the RBC product configuration. ifeq ($(RBC_PRODUCT_CONFIG)$(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),) # Import all the products that have made artifact path requirements, so that we can verify # the artifacts they produce. They might be intermediate makefiles instead of real products. $(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\ $(if $(filter-out $(makefile),$(PRODUCTS)),$(eval $(call import-products,$(makefile))))\ ) endif INTERNAL_PRODUCT := $(current_product_makefile) # Strip and assign the PRODUCT_ variables. $(call strip-product-vars) # Quick check $(check-current-product) ifneq ($(ALLOW_RULES_IN_PRODUCT_CONFIG),) .KATI_ALLOW_RULES := $(_saved_KATI_ALLOW_RULES) _product_config_saved_KATI_ALLOW_RULES := endif ############################################################################ current_product_makefile := # AOSP and Google products currently share the same `apex_contributions` in next. # This causes issues when building -next-userdebug in main. # Create a temporary allowlist to ignore the google apexes listed in `contents` of apex_contributions of `next` # *for aosp products*. # TODO(b/308187268): Remove this denylist mechanism # Use PRODUCT_PACKAGES to determine if this is an aosp product. aosp products do not use google signed apexes. ignore_apex_contributions := ifeq (,$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))$(findstring com.google.android.go.conscrypt,$(PRODUCT_PACKAGES))) ignore_apex_contributions := true endif ifeq (true,$(PRODUCT_MODULE_BUILD_FROM_SOURCE)) ignore_apex_contributions := true endif ifneq ($(EMMA_INSTRUMENT)$(EMMA_INSTRUMENT_STATIC)$(EMMA_INSTRUMENT_FRAMEWORK)$(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),) # Coverage builds for TARGET_RELEASE=foo should always build from source, # even if TARGET_RELEASE=foo uses prebuilt mainline modules. # This is necessary because the checked-in prebuilts were generated with # instrumentation turned off. ignore_apex_contributions := true endif ifeq (true, $(ignore_apex_contributions)) PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS := true endif ############################################################################# # Quick check and assign default values TARGET_DEVICE := $(PRODUCT_DEVICE) # Allow overriding PLATFORM_BASE_OS when PRODUCT_BASE_OS is defined ifdef PRODUCT_BASE_OS PLATFORM_BASE_OS := $(PRODUCT_BASE_OS) else PLATFORM_BASE_OS := $(PLATFORM_BASE_OS_ENV_INPUT) endif .KATI_READONLY := PLATFORM_BASE_OS # TODO: also keep track of things like "port", "land" in product files. # Figure out which resoure configuration options to use for this # product. # If CUSTOM_LOCALES contains any locales not already included # in PRODUCT_LOCALES, add them to PRODUCT_LOCALES. extra_locales := $(filter-out $(PRODUCT_LOCALES),$(CUSTOM_LOCALES)) ifneq (,$(extra_locales)) ifneq ($(CALLED_FROM_SETUP),true) # Don't spam stdout, because envsetup.sh may be scraping values from it. $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)]) endif PRODUCT_LOCALES += $(extra_locales) extra_locales := endif # Add PRODUCT_LOCALES to PRODUCT_AAPT_CONFIG PRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES) $(PRODUCT_AAPT_CONFIG) # Keep a copy of the space-separated config PRODUCT_AAPT_CONFIG_SP := $(PRODUCT_AAPT_CONFIG) PRODUCT_AAPT_CONFIG := $(subst $(space),$(comma),$(PRODUCT_AAPT_CONFIG)) ########################################################### ## Add 'platform:' prefix to jars not in : format. ## ## This makes sure that a jar corresponds to ConfigureJarList format of and pairs ## where needed. ## ## $(1): a list of jars either in or : format ########################################################### define qualify-platform-jars $(foreach jar,$(1),$(if $(findstring :,$(jar)),,platform:)$(jar)) endef # Extra boot jars must be appended at the end after common boot jars. PRODUCT_BOOT_JARS += $(PRODUCT_BOOT_JARS_EXTRA) PRODUCT_BOOT_JARS := $(call qualify-platform-jars,$(PRODUCT_BOOT_JARS)) # b/191127295: force core-icu4j onto boot image. It comes from a non-updatable APEX jar, but has # historically been part of the boot image; even though APEX jars are not meant to be part of the # boot image. # TODO(b/191686720): remove PRODUCT_APEX_BOOT_JARS to avoid a special handling of core-icu4j # in make rules. PRODUCT_APEX_BOOT_JARS := $(filter-out com.android.i18n:core-icu4j,$(PRODUCT_APEX_BOOT_JARS)) # All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list PRODUCT_BOOT_JARS += com.android.i18n:core-icu4j # The extra system server jars must be appended at the end after common system server jars. PRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA) PRODUCT_SYSTEM_SERVER_JARS := $(call qualify-platform-jars,$(PRODUCT_SYSTEM_SERVER_JARS)) # Sort APEX boot and system server jars. We use deterministic alphabetical order # when constructing BOOTCLASSPATH and SYSTEMSERVERCLASSPATH definition on device # after an update. Enforce it in the build system as well to avoid recompiling # everything after an update due a change in the order. PRODUCT_APEX_BOOT_JARS := $(sort $(PRODUCT_APEX_BOOT_JARS)) PRODUCT_APEX_SYSTEM_SERVER_JARS := $(sort $(PRODUCT_APEX_SYSTEM_SERVER_JARS)) PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \ $(call qualify-platform-jars,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS)) ifndef PRODUCT_SYSTEM_NAME PRODUCT_SYSTEM_NAME := $(PRODUCT_NAME) endif ifndef PRODUCT_SYSTEM_DEVICE PRODUCT_SYSTEM_DEVICE := $(PRODUCT_DEVICE) endif ifndef PRODUCT_SYSTEM_BRAND PRODUCT_SYSTEM_BRAND := $(PRODUCT_BRAND) endif ifndef PRODUCT_MODEL PRODUCT_MODEL := $(PRODUCT_NAME) endif ifndef PRODUCT_SYSTEM_MODEL PRODUCT_SYSTEM_MODEL := $(PRODUCT_MODEL) endif ifndef PRODUCT_MANUFACTURER PRODUCT_MANUFACTURER := unknown endif ifndef PRODUCT_SYSTEM_MANUFACTURER PRODUCT_SYSTEM_MANUFACTURER := $(PRODUCT_MANUFACTURER) endif ifndef PRODUCT_CHARACTERISTICS TARGET_AAPT_CHARACTERISTICS := default else TARGET_AAPT_CHARACTERISTICS := $(PRODUCT_CHARACTERISTICS) endif ifndef PRODUCT_SHIPPING_API_LEVEL PRODUCT_SHIPPING_API_LEVEL := 10000 endif ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE))) $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \ only 1 certificate is allowed.) endif endif $(foreach apexpair,$(PRODUCT_APEX_BOOT_JARS), \ $(foreach platformpair,$(PRODUCT_BOOT_JARS), \ $(eval apexjar := $(call word-colon,2,$(apexpair))) \ $(eval platformjar := $(call word-colon,2,$(platformpair))) \ $(if $(filter $(apexjar), $(platformjar)), \ $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(apexjar) is)))) ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT) ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST) PRODUCT_OTA_PUBLIC_KEYS := $(sort $(PRODUCT_OTA_PUBLIC_KEYS)) PRODUCT_EXTRA_OTA_KEYS := $(sort $(PRODUCT_EXTRA_OTA_KEYS)) PRODUCT_EXTRA_RECOVERY_KEYS := $(sort $(PRODUCT_EXTRA_RECOVERY_KEYS)) PRODUCT_VALIDATION_CHECKS := $(sort $(PRODUCT_VALIDATION_CHECKS)) # Resolve and setup per-module dex-preopt configs. DEXPREOPT_DISABLED_MODULES := # If a module has multiple setups, the first takes precedence. _pdpmc_modules := $(foreach c,$(PRODUCT_DEX_PREOPT_MODULE_CONFIGS),\ $(eval m := $(firstword $(subst =,$(space),$(c))))\ $(if $(filter $(_pdpmc_modules),$(m)),,\ $(eval _pdpmc_modules += $(m))\ $(eval cf := $(patsubst $(m)=%,%,$(c)))\ $(eval cf := $(subst $(_PDPMC_SP_PLACE_HOLDER),$(space),$(cf)))\ $(if $(filter disable,$(cf)),\ $(eval DEXPREOPT_DISABLED_MODULES += $(m)),\ $(eval DEXPREOPT.$(TARGET_PRODUCT).$(m).CONFIG := $(cf))))) _pdpmc_modules := # Resolve and setup per-module sanitizer configs. # If a module has multiple setups, the first takes precedence. _psmc_modules := $(foreach c,$(PRODUCT_SANITIZER_MODULE_CONFIGS),\ $(eval m := $(firstword $(subst =,$(space),$(c))))\ $(if $(filter $(_psmc_modules),$(m)),,\ $(eval _psmc_modules += $(m))\ $(eval cf := $(patsubst $(m)=%,%,$(c)))\ $(eval cf := $(subst $(_PSMC_SP_PLACE_HOLDER),$(space),$(cf)))\ $(eval SANITIZER.$(TARGET_PRODUCT).$(m).CONFIG := $(cf)))) _psmc_modules := # Reset ADB keys. If RELEASE_BUILD_USE_VARIANT_FLAGS is set look for # the value of a dedicated flag. Otherwise check if build variant is # non-debuggable. ifneq (,$(RELEASE_BUILD_USE_VARIANT_FLAGS)) ifneq (,$(RELEASE_BUILD_PURGE_PRODUCT_ADB_KEYS)) PRODUCT_ADB_KEYS := endif else ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT))) PRODUCT_ADB_KEYS := endif ifneq ($(filter-out 0 1,$(words $(PRODUCT_ADB_KEYS))),) $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS)) endif # Show a warning wall of text if non-compliance-GSI products set this option. ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64 gsi_tv_arm gsi_tv_arm64 clockwork_gsi_google_arm,$(PRODUCT_NAME))) $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \ PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \ testing. This is a special configuration for compliance GSI, so do make \ sure you understand the security implications before setting this \ option. If you don't know what this option does, then you probably \ shouldn't set this.) endif endif ifndef PRODUCT_USE_DYNAMIC_PARTITIONS PRODUCT_USE_DYNAMIC_PARTITIONS := $(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) endif # All requirements of PRODUCT_USE_DYNAMIC_PARTITIONS falls back to # PRODUCT_USE_DYNAMIC_PARTITIONS if not defined. ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE PRODUCT_USE_DYNAMIC_PARTITION_SIZE := $(PRODUCT_USE_DYNAMIC_PARTITIONS) endif ifndef PRODUCT_BUILD_SUPER_PARTITION PRODUCT_BUILD_SUPER_PARTITION := $(PRODUCT_USE_DYNAMIC_PARTITIONS) endif ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),) ifdef PRODUCT_SHIPPING_API_LEVEL ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29)) PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true endif endif endif ifeq ($(PRODUCT_SET_DEBUGFS_RESTRICTIONS),) ifdef PRODUCT_SHIPPING_API_LEVEL ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),31)) PRODUCT_SET_DEBUGFS_RESTRICTIONS := true endif endif endif # If build command defines OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS, # override PRODUCT_EXTRA_VNDK_VERSIONS with it. ifdef OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS PRODUCT_EXTRA_VNDK_VERSIONS := $(OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS) endif ########################################### # APEXes are by default not compressed # # APEX compression can be forcibly enabled (resp. disabled) by # setting OVERRIDE_PRODUCT_COMPRESSED_APEX to true (resp. false), e.g. by # setting the OVERRIDE_PRODUCT_COMPRESSED_APEX environment variable. ifdef OVERRIDE_PRODUCT_COMPRESSED_APEX PRODUCT_COMPRESSED_APEX := $(OVERRIDE_PRODUCT_COMPRESSED_APEX) endif ifdef OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := $(OVERRIDE_PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE) else ifeq ($(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE),) # Use ext4 as a default payload fs type PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE := ext4 endif ifeq ($(filter ext4 erofs,$(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)),) $(error PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE should be either erofs or ext4,\ not $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE).) endif $(KATI_obsolete_var OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS \ ,Use PRODUCT_EXTRA_VNDK_VERSIONS instead) # If build command defines OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE, # override PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE with it unless it is # defined as `false`. If the value is `false` clear # PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE # OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE can be used for # testing only. ifdef OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE ifeq (false,$(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE)) PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := else PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := $(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE) endif else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),) # No shipping level defined. Enforce the product interface by default. PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true else ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),29),true) # Enforce product interface if PRODUCT_SHIPPING_API_LEVEL is greater than 29. PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true endif $(KATI_obsolete_var OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE,Use PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE instead) # From Android V, Define PRODUCT_PRODUCT_VNDK_VERSION as current by default. # This is required to make all devices have product variants. ifndef PRODUCT_PRODUCT_VNDK_VERSION PRODUCT_PRODUCT_VNDK_VERSION := current endif ifdef PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS $(error PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS is deprecated, consider using RRO for \ $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS)) endif # This table maps sdk version 35 to vendor api level 202404 and assumes yearly # release for the same month. If 10000 API level or more is used, which usually # represents 'current' or 'future' API levels, several zeros are added to # preserve ordering. Specifically API level 10,000 is converted to 10,000,000 # which importantly is greater than 202404 = 202,404. This convention will break # in 100,000 CE, which is the closest multiple of 10 that doesn't break earlier # than 10,000 as an API level breaks. define sdk-to-vendor-api-level $(if $(call math_lt_or_eq,$(1),34),$(1),$(if $(call math_lt,$(1),10000),20$(call int_subtract,$(1),11)04,$(1)000)) endef ifneq ($(call sdk-to-vendor-api-level,34),34) $(error sdk-to-vendor-api-level is broken for pre-Trunk-Stable SDKs) endif ifneq ($(call sdk-to-vendor-api-level,35),202404) $(error sdk-to-vendor-api-level is broken for post-Trunk-Stable SDKs) endif ifneq ($(call sdk-to-vendor-api-level,10000),10000000) $(error sdk-to-vendor-api-level is broken for current $(call sdk-to-vendor-api-level,10000)) endif # VSR API level is the vendor api level of the product shipping API level. VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PLATFORM_SDK_VERSION)) ifdef PRODUCT_SHIPPING_API_LEVEL VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PRODUCT_SHIPPING_API_LEVEL)) endif ifdef BOARD_SHIPPING_API_LEVEL # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level. # In this case, the VSR API level is the minimum of the PRODUCT_SHIPPING_API_LEVEL # and RELEASE_BOARD_API_LEVEL board_api_level := $(RELEASE_BOARD_API_LEVEL) ifdef BOARD_API_LEVEL_PROP_OVERRIDE # This must be used only for testing purpose. Product must not be released # with the modified api level value. board_api_level := $(BOARD_API_LEVEL_PROP_OVERRIDE) endif VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(board_api_level)) board_api_level := endif .KATI_READONLY := VSR_VENDOR_API_LEVEL # Boolean variable determining if vendor seapp contexts is enforced CHECK_VENDOR_SEAPP_VIOLATIONS := false ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),34),) CHECK_VENDOR_SEAPP_VIOLATIONS := true else ifneq ($(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS),) CHECK_VENDOR_SEAPP_VIOLATIONS := $(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS) endif .KATI_READONLY := CHECK_VENDOR_SEAPP_VIOLATIONS # Boolean variable determining if selinux labels of /dev are enforced CHECK_DEV_TYPE_VIOLATIONS := false ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),202404),) CHECK_DEV_TYPE_VIOLATIONS := true else ifneq ($(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS),) CHECK_DEV_TYPE_VIOLATIONS := $(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS) endif .KATI_READONLY := CHECK_DEV_TYPE_VIOLATIONS define product-overrides-config $$(foreach rule,$$(PRODUCT_$(1)_OVERRIDES),\ $$(if $$(filter 2,$$(words $$(subst :,$$(space),$$(rule)))),,\ $$(error Rule "$$(rule)" in PRODUCT_$(1)_OVERRIDE is not :))) endef $(foreach var, \ MANIFEST_PACKAGE_NAME \ PACKAGE_NAME \ CERTIFICATE, \ $(eval $(call product-overrides-config,$(var)))) # Macro to use below. $(1) is the name of the partition define product-build-image-config ifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),) $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported) endif endef ifndef PRODUCT_VIRTUAL_AB_COW_VERSION PRODUCT_VIRTUAL_AB_COW_VERSION := 2 ifdef PRODUCT_SHIPPING_API_LEVEL ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),34)) PRODUCT_VIRTUAL_AB_COW_VERSION := 3 endif endif endif # Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable $(foreach image, \ PVMFW \ SYSTEM \ SYSTEM_OTHER \ VENDOR \ PRODUCT \ SYSTEM_EXT \ ODM \ VENDOR_DLKM \ ODM_DLKM \ SYSTEM_DLKM \ CACHE \ RAMDISK \ USERDATA \ BOOT \ RECOVERY, \ $(eval $(call product-build-image-config,$(image)))) product-build-image-config := ifdef PRODUCT_SOONG_ONLY ifneq ($(PRODUCT_SOONG_ONLY),true) ifneq ($(PRODUCT_SOONG_ONLY),false) $(error PRODUCT_SOONG_ONLY can only be true, false or unset) endif endif endif $(call readonly-product-vars) ================================================ FILE: core/product_config.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Runtime functions.""" _soong_config_namespaces_key = "$SOONG_CONFIG_NAMESPACES" _dist_for_goals_key = "$dist_for_goals" def _init_globals(input_variables_init): """Initializes dictionaries of global variables. This function runs the given input_variables_init function, passing it a globals dictionary and a handle as if it were a regular product. It then returns 2 copies of the globals dictionary, so that one can be kept around to diff changes made to the other later. """ globals_base = {"PRODUCT_SOONG_NAMESPACES": []} input_variables_init(globals_base, __h_new()) # Rerun input_variables_init to produce a copy # of globals_base, because starlark doesn't support # deep copying objects. globals = {"PRODUCT_SOONG_NAMESPACES": []} input_variables_init(globals, __h_new()) # Variables that should be defined. mandatory_vars = [ "PLATFORM_VERSION_CODENAME", "PLATFORM_VERSION", "PRODUCT_SOONG_NAMESPACES", # TODO(asmundak): do we need TARGET_ARCH? AOSP does not reference it "TARGET_BUILD_VARIANT", "TARGET_PRODUCT", ] for bv in mandatory_vars: if not bv in globals: fail(bv, " is not defined") return (globals, globals_base) def __print_attr(attr, value): # Allow using empty strings to clear variables, but not None values if value == None: return if type(value) == "list": value = list(value) for i, x in enumerate(value): if type(x) == "tuple" and len(x) == 1: value[i] = "@inherit:" + x[0] + ".mk" elif type(x) != "string": fail("Wasn't a list of strings:", attr, " value:", value) print(attr, ":=", " ".join(value)) else: # Trim all spacing to a single space print(attr, ":=", _mkstrip(value)) def _printvars(state): """Prints configuration and global variables.""" (globals, globals_base) = state for attr, val in sorted(globals.items()): if attr == _soong_config_namespaces_key: __print_attr("SOONG_CONFIG_NAMESPACES", val.keys()) for nsname, nsvars in sorted(val.items()): # Define SOONG_CONFIG_ for Make, othewise # it cannot be added to .KATI_READONLY list print("SOONG_CONFIG_" + nsname, ":=", " ".join(nsvars.keys())) for var, val in sorted(nsvars.items()): if val: __print_attr("SOONG_CONFIG_%s_%s" % (nsname, var), val) else: print("SOONG_CONFIG_%s_%s :=" % (nsname, var)) elif attr == _dist_for_goals_key: goals = [] src_dst_list = [] goal_dst_list = [] for goal_name, goal_src_dst_list in sorted(val.items()): goals.append(goal_name) for sd in sorted(goal_src_dst_list): src_dst_list.append(":".join(sd)) goal_dst_list.append(":".join((goal_name, sd[1]))) print("_all_dist_goal_output_pairs:=", " ".join(goal_dst_list)) print("_all_dist_goals:=", " ".join(goals)) print("_all_dist_src_dst_pairs:=", " ".join(src_dst_list)) elif attr not in globals_base or globals_base[attr] != val: __print_attr(attr, val) def __sort_pcm_names(pcm_names): # We have to add an extension back onto the pcm names when sorting, # or else the sort order could be wrong when one is a prefix of another. return [x[:-3] for x in sorted([y + ".mk" for y in pcm_names], reverse=True)] def _product_configuration(top_pcm_name, top_pcm, input_variables_init): """Creates configuration.""" # Product configuration is created by traversing product's inheritance # tree. It is traversed twice. # First, beginning with top-level module we execute a module and find # its ancestors, repeating this recursively. At the end of this phase # we get the full inheritance tree. # Second, we traverse the tree in the postfix order (i.e., visiting a # node after its ancestors) to calculate the product configuration. # # PCM means "Product Configuration Module", i.e., a Starlark file # whose body consists of a single init function. globals, globals_base = _init_globals(input_variables_init) # Each PCM is represented by a quadruple of function, config, children names # and readyness (that is, the configurations from inherited PCMs have been # substituted). configs = {top_pcm_name: (top_pcm, None, [], False)} # All known PCMs # Stack containing PCMs to be processed pcm_stack = [top_pcm_name] # Run it until pcm_stack is exhausted, but no more than N times for n in range(1000): if not pcm_stack: break name = pcm_stack.pop() pcm, cfg, c, _ = configs[name] # cfg is set only after PCM has been called, leverage this # to prevent calling the same PCM twice if cfg != None: continue # Run this one, obtaining its configuration and child PCMs. if _options.trace_modules: rblf_log("%d: %s" % (n, name)) # Run PCM. handle = __h_new() pcm(globals, handle) if handle.artifact_path_requirements: globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_REQUIREMENTS"] = handle.artifact_path_requirements globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_ALLOWED_LIST"] = handle.artifact_path_allowed_list globals["PRODUCTS."+name+".mk.ARTIFACT_PATH_REQUIREMENT_IS_RELAXED"] = "true" if handle.artifact_path_requirement_is_relaxed[0] else "" globals.setdefault("ARTIFACT_PATH_REQUIREMENT_PRODUCTS", []) globals["ARTIFACT_PATH_REQUIREMENT_PRODUCTS"] = sorted(globals["ARTIFACT_PATH_REQUIREMENT_PRODUCTS"] + [name+".mk"]) if handle.product_enforce_packages_exist[0]: globals["PRODUCTS."+name+".mk.PRODUCT_ENFORCE_PACKAGES_EXIST"] = "true" globals["PRODUCTS."+name+".mk.PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST"] = handle.product_enforce_packages_exist_allow_list # Now we know everything about this PCM, record it in 'configs'. children = handle.inherited_modules if _options.trace_modules: rblf_log(" ", " ".join(children.keys())) # Starlark dictionaries are guaranteed to iterate through in insertion order, # so children.keys() will be ordered by the inherit() calls configs[name] = (pcm, handle.cfg, children.keys(), False) for child_name in __sort_pcm_names(children.keys()): if child_name not in configs: configs[child_name] = (children[child_name], None, [], False) pcm_stack.append(child_name) if pcm_stack: fail("Inheritance processing took too many iterations") for pcm_name in globals.get("ARTIFACT_PATH_REQUIREMENT_PRODUCTS", []): for var, val in evaluate_finalized_product_variables(configs, pcm_name[:-3]).items(): globals["PRODUCTS."+pcm_name+"."+var] = val # Copy product config variables from the cfg dictionary to the # PRODUCTS.. global variables. for var, val in evaluate_finalized_product_variables(configs, top_pcm_name, _options.trace_modules).items(): globals["PRODUCTS."+top_pcm_name+".mk."+var] = val # Record inheritance hierarchy in PRODUCTS..INHERITS_FROM variables. # This is required for m product-graph. for config in configs: if len(configs[config][2]) > 0: globals["PRODUCTS."+config+".mk.INHERITS_FROM"] = sorted([x + ".mk" for x in configs[config][2]]) globals["PRODUCTS"] = __words(globals.get("PRODUCTS", [])) + [top_pcm_name + ".mk"] return (globals, globals_base) def evaluate_finalized_product_variables(configs, top_level_pcm_name, trace=False): configs_postfix = [] pcm_stack = [(top_level_pcm_name, True)] for i in range(1000): if not pcm_stack: break pcm_name, before = pcm_stack.pop() if before: pcm_stack.append((pcm_name, False)) for child in __sort_pcm_names(configs[pcm_name][2]): pcm_stack.append((child, True)) else: configs_postfix.append(pcm_name) if pcm_stack: fail("Inheritance processing took too many iterations") # clone the configs, because in the process of evaluating the # final cfg dictionary we will remove values from the intermediate # cfg dictionaries. We need to be able to call evaluate_finalized_product_variables() # multiple times, so we can't change the origional configs object. cloned_configs = {} for pcm_name in configs: # skip unneeded pcms if pcm_name not in configs_postfix: continue pcm, cfg, children_names, ready = configs[pcm_name] cloned_cfg = {} for var, val in cfg.items(): if type(val) == 'list': cloned_cfg[var] = list(val) else: cloned_cfg[var] = val cloned_configs[pcm_name] = (pcm, cloned_cfg, children_names, ready) configs = cloned_configs if trace: rblf_log("\n---Postfix---") for x in configs_postfix: rblf_log(" ", x) # Traverse the tree from the bottom, evaluating inherited values for pcm_name in configs_postfix: pcm, cfg, children_names, ready = configs[pcm_name] # Should run if cfg == None: fail("%s: has not been run" % pcm_name) # Ready once if ready: continue # Children should be ready for child_name in children_names: if not configs[child_name][3]: fail("%s: child is not ready" % child_name) _substitute_inherited(configs, pcm_name, cfg) _percolate_inherited(configs, pcm_name, cfg, children_names) configs[pcm_name] = pcm, cfg, children_names, True return configs[top_level_pcm_name][1] def _dictionary_difference(a, b): result = {} for attr, val in a.items(): if attr not in b or b[attr] != val: result[attr] = val return result def _board_configuration(board_config_init, input_variables_init): globals_base = {} h_base = __h_new() globals = {} h = __h_new() input_variables_init(globals_base, h_base) input_variables_init(globals, h) board_config_init(globals, h) # Board configuration files aren't really supposed to change # product configuration variables, but some do. You lose the # inheritance features of the product config variables if you do. for var, value in _dictionary_difference(h.cfg, h_base.cfg).items(): globals[var] = value return (globals, globals_base) def _substitute_inherited(configs, pcm_name, cfg): """Substitutes inherited values in all the attributes. When a value of an attribute is a list, some of its items may be references to a value of a same attribute in an inherited product, e.g., for a given module PRODUCT_PACKAGES can be ["foo", (submodule), "bar"] and for 'submodule' PRODUCT_PACKAGES may be ["baz"] (we use a tuple to distinguish submodule references). After the substitution the value of PRODUCT_PACKAGES for the module will become ["foo", "baz", "bar"] """ for attr, val in cfg.items(): # TODO(asmundak): should we handle single vars? if type(val) != "list": continue if attr not in _options.trace_variables: cfg[attr] = _value_expand(configs, attr, val) else: old_val = val new_val = _value_expand(configs, attr, val) if new_val != old_val: rblf_log("%s(i): %s=%s (was %s)" % (pcm_name, attr, new_val, old_val)) cfg[attr] = new_val def _value_expand(configs, attr, values_list): """Expands references to inherited values in a given list.""" result = [] expanded = {} for item in values_list: # Inherited values are 1-tuples if type(item) != "tuple": result.append(item) continue child_name = item[0] if child_name in expanded: continue expanded[child_name] = True child = configs[child_name] if not child[3]: fail("%s should be ready" % child_name) __move_items(result, child[1], attr) return result def _percolate_inherited(configs, cfg_name, cfg, children_names): """Percolates the settings that are present only in children.""" percolated_attrs = {} for child_name in children_names: child_cfg = configs[child_name][1] for attr, value in child_cfg.items(): if type(value) != "list": continue if attr in percolated_attrs: # We already are percolating this one, just add this list __move_items(cfg[attr], child_cfg, attr) elif not attr in cfg: percolated_attrs[attr] = True cfg[attr] = [] __move_items(cfg[attr], child_cfg, attr) # single value variables need to be inherited in alphabetical order, # not in the order of inherit() calls. for child_name in sorted(children_names): child_cfg = configs[child_name][1] for attr, value in child_cfg.items(): if type(value) != "list": # Single value variables take the first value available from the leftmost # branch of the tree. If we also had "or attr in percolated_attrs" in this # if statement, it would take the value from the rightmost branch. if cfg.get(attr, "") == "": cfg[attr] = value percolated_attrs[attr] = True child_cfg.pop(attr) for attr in _options.trace_variables: if attr in percolated_attrs: rblf_log("%s: %s^=%s" % (cfg_name, attr, cfg[attr])) def __move_items(to_list, from_cfg, attr): value = from_cfg.get(attr, []) if value: to_list.extend(value) from_cfg.pop(attr) def _indirect(pcm_name): """Returns configuration item for the inherited module.""" return (pcm_name,) def _soong_config_namespace(g, nsname): """Adds given namespace if it does not exist.""" old = g.get(_soong_config_namespaces_key, {}) if old.get(nsname): return # A value cannot be updated, so we need to create a new dictionary g[_soong_config_namespaces_key] = dict([(k,v) for k,v in old.items()] + [(nsname, {})]) def _soong_config_set(g, nsname, var, value): """Assigns the value to the variable in the namespace.""" _soong_config_namespace(g, nsname) g[_soong_config_namespaces_key][nsname][var]=_mkstrip(value) def _soong_config_set_bool(g, nsname, var, value): """Assigns the value to the variable in the namespace, and marks it as a boolean.""" _soong_config_set(g, nsname, var, _filter("true", value)) g["SOONG_CONFIG_TYPE_%s_%s" % (nsname, var)] = "bool" def _soong_config_append(g, nsname, var, value): """Appends to the value of the variable in the namespace.""" _soong_config_namespace(g, nsname) ns = g[_soong_config_namespaces_key][nsname] oldv = ns.get(var) if oldv == None: ns[var] = _mkstrip(value) else: ns[var] += " " + _mkstrip(value) def _soong_config_get(g, nsname, var): """Gets to the value of the variable in the namespace.""" return g.get(_soong_config_namespaces_key, {}).get(nsname, {}).get(var, None) def _abspath(paths): """Provided for compatibility, to be removed later.""" cwd = rblf_shell('pwd') results = [] for path in __words(paths): if path[0] != "/": path = cwd + "/" + path resultparts = [] for part in path.split('/'): if part == "." or part == "": continue elif part == "..": if resultparts: resultparts.pop() else: resultparts.append(part) results.append("/" + "/".join(resultparts)) return " ".join(results) def _addprefix(prefix, string_or_list): """Adds prefix and returns a list. If string_or_list is a list, prepends prefix to each element. Otherwise, string_or_list is considered to be a string which is split into words and then prefix is prepended to each one. Args: prefix string_or_list """ return [prefix + x for x in __words(string_or_list)] def _addsuffix(suffix, string_or_list): """Adds suffix and returns a list. If string_or_list is a list, appends suffix to each element. Otherwise, string_or_list is considered to be a string which is split into words and then suffix is appended to each one. Args: suffix string_or_list """ return [x + suffix for x in __words(string_or_list)] def __words(string_or_list): if type(string_or_list) == "list": for x in string_or_list: if type(x) != "string": return string_or_list string_or_list = " ".join(string_or_list) return _mkstrip(string_or_list).split() # Handle manipulation functions. # A handle passed to a PCM consists of: # product attributes dict ("cfg") # inherited modules dict (maps module name to PCM) # default value list (initially empty, modified by inheriting) def __h_new(): """Constructs a handle which is passed to PCM.""" return struct( cfg = dict(), inherited_modules = dict(), default_list_value = list(), artifact_path_requirements = list(), artifact_path_allowed_list = list(), artifact_path_requirement_is_relaxed = [False], # as a list so that we can reassign it product_enforce_packages_exist = [False], product_enforce_packages_exist_allow_list = [], ) def __h_cfg(handle): """Returns PCM's product configuration attributes dict. This function is also exported as rblf.cfg, and every PCM calls it at the beginning. """ return handle.cfg def _setdefault(handle, attr): """If attribute has not been set, assigns default value to it. This function is exported as rblf.setdefault(). Only list attributes are initialized this way. The default value is kept in the PCM's handle. Calling inherit() updates it. """ cfg = handle.cfg if cfg.get(attr) == None: cfg[attr] = list(handle.default_list_value) return cfg[attr] def _inherit(handle, pcm_name, pcm): """Records inheritance. This function is exported as rblf.inherit, PCM calls it when a module is inherited. """ handle.inherited_modules[pcm_name] = pcm handle.default_list_value.append(_indirect(pcm_name)) # Add inherited module reference to all configuration values for attr, val in handle.cfg.items(): if type(val) == "list": val.append(_indirect(pcm_name)) def __base(path): """Returns basename.""" return path.rsplit("/",1)[-1] def _board_platform_in(g, string_or_list): """Returns true if board is in the list.""" board = g.get("TARGET_BOARD_PLATFORM","") if not board: return False return board in __words(string_or_list) def _board_platform_is(g, s): """True if board is the same as argument.""" return g.get("TARGET_BOARD_PLATFORM","") == s def _copy_files(l, outdir): """Generate :/item for each item.""" return ["%s:%s/%s" % (path, outdir, __base(path)) for path in __words(l)] def _copy_if_exists(path_pair): """If from file exists, returns [from:to] pair.""" value = path_pair.split(":", 2) if value[0].find('*') != -1: fail("copy_if_exists: input file cannot contain *") # Check that l[0] exists return [":".join(value)] if rblf_wildcard(value[0]) else [] def _enforce_product_packages_exist(handle, pkg_string_or_list=[]): """Makes including non-existent modules in PRODUCT_PACKAGES an error.""" handle.product_enforce_packages_exist[0] = True handle.product_enforce_packages_exist_allow_list.clear() handle.product_enforce_packages_exist_allow_list.extend(__words(pkg_string_or_list)) def _add_product_dex_preopt_module_config(handle, modules, config): """Equivalent to add-product-dex-preopt-module-config from build/make/core/product.mk.""" modules = __words(modules) config = _mkstrip(config).replace(" ", "|@SP@|") _setdefault(handle, "PRODUCT_DEX_PREOPT_MODULE_CONFIGS") handle.cfg["PRODUCT_DEX_PREOPT_MODULE_CONFIGS"] += [m + "=" + config for m in modules] def _find_and_copy(pattern, from_dir, to_dir): """Return a copy list for the files matching the pattern.""" return sorted([("%s/%s:%s/%s" % (from_dir, f, to_dir, f)) .replace("//", "/") for f in rblf_find_files(from_dir, pattern, only_files=1)]) def _findstring(needle, haystack): """Equivalent to GNU make's $(findstring).""" if haystack.find(needle) < 0: return "" return needle def _filter_out(pattern, text): """Return all the words from `text' that do not match any word in `pattern'. Args: pattern: string or list of words. '%' stands for wildcard (in regex terms, '.*') text: string or list of words Return: list of words """ patterns = [__mkparse_pattern(x) for x in __words(pattern)] res = [] for w in __words(text): match = False for p in patterns: if __mkpattern_matches(p, w): match = True break if not match: res.append(w) return res def _filter(pattern, text): """Return all the words in `text` that match `pattern`. Args: pattern: strings of words or a list. A word can contain '%', which stands for any sequence of characters. text: string or list of words. """ patterns = [__mkparse_pattern(x) for x in __words(pattern)] res = [] for w in __words(text): for p in patterns: if __mkpattern_matches(p, w): res.append(w) break return res def _first_word(input): """Equivalent to the GNU make function $(firstword).""" input = __words(input) if len(input) == 0: return "" return input[0] def _last_word(input): """Equivalent to the GNU make function $(lastword).""" input = __words(input) l = len(input) if l == 0: return "" return input[l-1] def _flatten_2d_list(list): result = [] for x in list: result += x return result def _dir(paths): """Equivalent to the GNU make function $(dir). Returns the folder of the file for each path in paths. """ return " ".join([w.rsplit("/",1)[0] for w in __words(paths)]) def _notdir(paths): """Equivalent to the GNU make function $(notdir). Returns the name of the file at the end of each path in paths. """ return " ".join([__base(w) for w in __words(paths)]) def _require_artifacts_in_path(handle, paths, allowed_paths): """Equivalent to require-artifacts-in-path in Make.""" handle.artifact_path_requirements.clear() handle.artifact_path_requirements.extend(__words(paths)) handle.artifact_path_allowed_list.clear() handle.artifact_path_allowed_list.extend(__words(allowed_paths)) def _require_artifacts_in_path_relaxed(handle, paths, allowed_paths): """Equivalent to require-artifacts-in-path-relaxed in Make.""" _require_artifacts_in_path(handle, paths, allowed_paths) handle.artifact_path_requirement_is_relaxed[0] = True def _expand_wildcard(pattern): """Expands shell wildcard pattern.""" result = [] for word in __words(pattern): result.extend(rblf_wildcard(word)) return result def _mkdist_for_goals(g, goal, src_dst_list): """Implements dist-for-goals macro.""" goals_map = g.get(_dist_for_goals_key, {}) pairs = goals_map.get(goal) if pairs == None: pairs = [] g[_dist_for_goals_key] = dict([(k,v) for k,v in goals_map.items()] + [(goal, pairs)]) for src_dst in __words(src_dst_list): pair=src_dst.split(":") if len(pair) > 2: fail(src_dst + " should be a :-separated pair") pairs.append((pair[0],pair[1] if len(pair) == 2 and pair[1] else __base(pair[0]))) g[_dist_for_goals_key][goal] = pairs def _mkerror(file, message = ""): """Prints error and stops.""" fail("%s: %s. Stop" % (file, message)) def _mkwarning(file, message = ""): """Prints warning.""" rblf_log(file, "warning", message, sep = ':') def _mk2rbc_error(loc, message): """Prints a message about conversion error and stops.""" _mkerror(loc, message) def _mkinfo(file, message = ""): """Prints info.""" rblf_log(message) def __mkparse_pattern(pattern): """Parses Make's patsubst pattern. This is equivalent to pattern.split('%', 1), except it also takes into account escaping the % symbols. """ in_escape = False res = [] acc = "" for c in pattern.elems(): if in_escape: in_escape = False acc += c elif c == '\\': in_escape = True elif c == '%' and not res: res.append(acc) acc = '' else: acc += c if in_escape: acc += '\\' res.append(acc) return res def __mkpattern_matches(pattern, word): """Returns if a pattern matches a given word. The pattern must be a list of strings of length at most 2. This checks if word is either equal to the pattern or starts/ends with the two parts of the pattern. """ if len(pattern) > 2: fail("Pattern can have at most 2 components") elif len(pattern) == 1: return pattern[0]==word else: return ((len(word) >= len(pattern[0])+len(pattern[1])) and word.startswith(pattern[0]) and word.endswith(pattern[1])) def __mkpatsubst_word(parsed_pattern,parsed_subst, word): (before, after) = parsed_pattern if not word.startswith(before): return word if not word.endswith(after): return word if len(parsed_subst) < 2: return parsed_subst[0] return parsed_subst[0] + word[len(before):len(word) - len(after)] + parsed_subst[1] def _mkpatsubst(pattern, replacement, s): """Emulates Make's patsubst. Tokenizes `s` (unless it is already a list), and then performs a simple wildcard substitution (in other words, `foo%bar` pattern is equivalent to the regular expression `^foo(.*)bar$, and the first `%` in replacement is $1 in regex terms). """ parsed_pattern = __mkparse_pattern(pattern) if len(parsed_pattern) == 1: out_words = [ replacement if x == pattern else x for x in __words(s)] else: parsed_replacement = __mkparse_pattern(replacement) out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in __words(s)] return out_words if type(s) == "list" else " ".join(out_words) def _mksort(input): """Emulate Make's sort. This is unique from a regular sort in that it also strips the input, and removes duplicate words from the input. """ input = sorted(__words(input)) result = [] for w in input: if len(result) == 0 or result[-1] != w: result.append(w) return result def _mkstrip(s): """Emulates Make's strip. That is, removes string's leading and trailing whitespace characters and replaces any sequence of whitespace characters with with a single space. """ t = type(s) if t == "list": s = " ".join(s) elif t != "string": fail("Argument to mkstrip must be a string or list, got: "+t) result = "" was_space = False for ch in s.strip().elems(): is_space = ch.isspace() if not is_space: if was_space: result += " " result += ch was_space = is_space return result def _mksubst(old, new, s): """Emulates Make's subst. Replaces each occurence of 'old' with 'new'. If 's' is a list, applies substitution to each item. """ if type(s) == "list": return [e.replace(old, new) for e in s] return s.replace(old, new) def _product_copy_files_by_pattern(src, dest, s): """Creates a copy list. For each item in a given list, create : pair, where and are the results of applying Make-style patsubst of and respectively. E.g. the result of calling this function with ("foo/%", "bar/%", ["a", "b"]) will be ["foo/a:bar/a", "foo/b:bar/b"]. """ parsed_src = __mkparse_pattern(src) parsed_dest = __mkparse_pattern(dest) parsed_percent = ["", ""] words = s if type(s) == "list" else _mkstrip(s).split(" ") return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + ":" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words] __zero_values = { "string": "", "list": [], "int": 0, "float": 0, "bool": False, "dict": {}, "NoneType": None, "tuple": (), } def __zero_value(x): t = type(x) if t in __zero_values: return __zero_values[t] else: fail("Unknown type: "+t) def _clear_var_list(g, h, var_list): cfg = __h_cfg(h) for v in __words(var_list): # Set these variables to their zero values rather than None # or removing them from the dictionary because if they were # removed entirely, ?= would set their value, when it would not # after a make-based clear_var_list call. if v in g: g[v] = __zero_value(g[v]) if v in cfg: cfg[v] = __zero_value(cfg[v]) if v not in cfg and v not in g: # Cause the variable to appear set like the make version does g[v] = "" # Settings used during debugging. _options = struct( trace_modules = False, trace_variables = [], ) rblf = struct( soong_config_namespace = _soong_config_namespace, soong_config_append = _soong_config_append, soong_config_set = _soong_config_set, soong_config_set_bool = _soong_config_set_bool, soong_config_get = _soong_config_get, abspath = _abspath, add_product_dex_preopt_module_config = _add_product_dex_preopt_module_config, addprefix = _addprefix, addsuffix = _addsuffix, board_platform_in = _board_platform_in, board_platform_is = _board_platform_is, clear_var_list = _clear_var_list, copy_files = _copy_files, copy_if_exists = _copy_if_exists, cfg = __h_cfg, dir = _dir, enforce_product_packages_exist = _enforce_product_packages_exist, expand_wildcard = _expand_wildcard, filter = _filter, filter_out = _filter_out, find_and_copy = _find_and_copy, findstring = _findstring, first_word = _first_word, last_word = _last_word, flatten_2d_list = _flatten_2d_list, inherit = _inherit, indirect = _indirect, mk2rbc_error = _mk2rbc_error, mkdist_for_goals = _mkdist_for_goals, mkinfo = _mkinfo, mkerror = _mkerror, mkpatsubst = _mkpatsubst, mkwarning = _mkwarning, mksort = _mksort, mkstrip = _mkstrip, mksubst = _mksubst, notdir = _notdir, printvars = _printvars, product_configuration = _product_configuration, board_configuration = _board_configuration, product_copy_files_by_pattern = _product_copy_files_by_pattern, require_artifacts_in_path = _require_artifacts_in_path, require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed, setdefault = _setdefault, shell = rblf_shell, warning = _mkwarning, words = __words, ) ================================================ FILE: core/product_validation_checks.mk ================================================ # PRODUCT_VALIDATION_CHECKS allows you to enforce that your product config variables follow some # rules. To use it, add the paths to starlark configuration language (scl) files in # PRODUCT_VALIDATION_CHECKS. A validate_product_variables function in those files will be called # with a single "context" object. # # The context object currently 2 attributes: # - product_variables: This has all the product variables. All the variables are either of type # string or list, more accurate typing (like bool) isn't known. # - board_variables: This only has a small subset of the board variables, because there isn't a # known list of board variables. Feel free to expand the subset if you need a # new variable. # # You can then inspect (but not modify) these variables and fail() if they don't meet your # requirements. Example: # # In a product config file: PRODUCT_VALIDATION_CHECKS += //path/to/my_validations.scl # In my_validations.scl: # def validate_product_variables(ctx): # for dir in ctx.board_variables.BOARD_SEPOLICY_DIRS: # if not dir.startswith('system/sepolicy/'): # fail('Only sepolicies in system/seplicy are allowed, found: ' + dir) ifdef PRODUCT_VALIDATION_CHECKS $(if $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS)), \ $(error All PRODUCT_VALIDATION_CHECKS files must start with // and end with .scl, exceptions: $(filter-out //%.scl,$(PRODUCT_VALIDATION_CHECKS)))) known_board_variables := \ BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \ SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \ SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \ PRODUCT_PUBLIC_SEPOLICY_DIRS \ PRODUCT_PRIVATE_SEPOLICY_DIRS known_board_list_variables := \ BOARD_VENDOR_SEPOLICY_DIRS BOARD_SEPOLICY_DIRS \ SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS \ SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS \ PRODUCT_PUBLIC_SEPOLICY_DIRS \ PRODUCT_PRIVATE_SEPOLICY_DIRS escape_starlark_string=$(subst ",\",$(subst \,\\,$(1))) product_variable_starlark_value=$(if $(filter $(1),$(_product_list_vars) $(known_board_list_variables)),[$(foreach w,$($(1)),"$(call escape_starlark_string,$(w))", )],"$(call escape_starlark_string,$(1))") filename_to_starlark=$(subst -,_,$(subst /,_,$(subst .,_,$(1)))) _c:=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)load("$(f)", validate_product_variables_$(call filename_to_starlark,$(f)) = "validate_product_variables")) # TODO: we should freeze the context because it contains mutable lists, so that validation checks can't affect each other _c+=$(newline)_ctx = struct( _c+=$(newline)product_variables = struct( _c+=$(foreach v,$(_product_var_list),$(newline) $(v) = $(call product_variable_starlark_value,$(v)),) _c+=$(newline)), _c+=$(newline)board_variables = struct( _c+=$(foreach v,$(known_board_variables),$(newline) $(v) = $(call product_variable_starlark_value,$(v)),) _c+=$(newline)) _c+=$(newline)) _c+=$(foreach f,$(PRODUCT_VALIDATION_CHECKS),$(newline)validate_product_variables_$(call filename_to_starlark,$(f))(_ctx)) _c+=$(newline)variables_to_export_to_make = {} $(KATI_file_no_rerun >$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(_c)) filename_to_starlark:= escape_starlark_string:= product_variable_starlark_value:= known_board_variables := known_board_list_variables := # Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't # rerun kati every build. Even though we're using KATI_file_no_rerun, product config is run every # build, so the file will still be rewritten. # # We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something # outside of the source tree. $(call run-starlark,$(OUT_DIR)/product_validation_checks_entrypoint.scl,$(OUT_DIR)/product_validation_checks_entrypoint.scl,--allow_external_entrypoint) endif # ifdef PRODUCT_VALIDATION_CHECKS ================================================ FILE: core/proguard/checknotnull.flags ================================================ # Tell R8 that the following methods are check not null methods, and to # replace invocations to them with a more concise nullness check that produces # (slightly) less informative error messages -convertchecknotnull class com.google.common.base.Preconditions { ** checkNotNull(...); } -convertchecknotnull class java.util.Objects { ** requireNonNull(...); } -convertchecknotnull class kotlin.jvm.internal.Intrinsics { void checkNotNull(...); void checkExpressionValueIsNotNull(...); void checkNotNullExpressionValue(...); void checkReturnedValueIsNotNull(...); void checkFieldIsNotNull(...); void checkParameterIsNotNull(...); void checkNotNullParameter(...); } -convertchecknotnull class dagger.internal.Preconditions { ** checkNotNull*(...); } ================================================ FILE: core/proguard/kotlin.flags ================================================ # Ignore missing Kotlin meta-annotations so that Java-only projects can depend # on projects that happen to be written in Kotlin but do not have a run-time # dependency on the Kotlin standard library. Note these annotations are RUNTIME # retention, but we won't need them available in Java-only projects. -dontwarn kotlin.Metadata -dontwarn kotlin.annotation.AnnotationRetention -dontwarn kotlin.annotation.AnnotationTarget -dontwarn kotlin.annotation.Retention -dontwarn kotlin.annotation.Target # Kotlin DebugMetadata has no value in release builds, these two rules, will # allow AppReduce to strip out DebutMetadata. # TODO(b/302383328): Restore the below checkdiscard after resolving transitive # inclusion of kotlin-stdlib from androidx.annotation library deps. # -checkdiscard interface kotlin.coroutines.jvm.internal.DebugMetadata -assumenosideeffects class kotlin.coroutines.jvm.internal.DebugMetadataKt { *** getDebugMetadataAnnotation(...); } -assumevalues class kotlin.coroutines.jvm.internal.DebugMetadataKt { *** getDebugMetadataAnnotation(...) return null; } ================================================ FILE: core/proguard.flags ================================================ # Keep classes and members with the platform-defined @VisibleForTesting annotation. -keep @com.android.internal.annotations.VisibleForTesting class * -keepclassmembers class * { @com.android.internal.annotations.VisibleForTesting *; } # Keep classes and members with platform @TestApi annotations, similar to # @VisibleForTesting. -keep @android.annotation.TestApi class * -keepclassmembers class * { @android.annotation.TestApi *; } # Keep classes and members with non-platform @VisibleForTesting annotations, but # only within platform-defined packages. This avoids keeping external, library-specific # test code that isn't actually needed for platform testing. # TODO(b/239961360): Migrate away from androidx.annotation.VisibleForTesting # and com.google.common.annotations.VisibleForTesting use in platform code. -keep @**.VisibleForTesting class android.**,com.android.**,com.google.android.** -keepclassmembers class android.**,com.android.**,com.google.android.** { @**.VisibleForTesting *; } # Keep rule for members that are needed solely to keep alive downstream weak # references, and could otherwise be removed after tree shaking optimizations. -keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * { @com.android.internal.annotations.KeepForWeakReference ; } # Needed to ensure callback field references are kept in their respective # owning classes when the downstream callback registrars only store weak refs. -if @com.android.internal.annotations.WeaklyReferencedCallback class * -keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * { !synthetic <1> *; } -if class * extends @com.android.internal.annotations.WeaklyReferencedCallback ** -keepclassmembers,allowaccessmodification,allowobfuscation,allowshrinking class * { !synthetic <1> *; } # Understand the common @Keep annotation from various Android packages: # * android.support.annotation # * androidx.annotation # * com.android.internal.annotations -keep class **android**.annotation*.Keep -keep @**android**.annotation*.Keep class * { *; } -keepclasseswithmembers class * { @**android**.annotation*.Keep ; } -keepclasseswithmembers class * { @**android**.annotation*.Keep ; } -keepclasseswithmembers class * { @**android**.annotation*.Keep (...); } # Keep Dalvik optimization annotations. These annotations are special in that # 1) we want them preserved for visibility with ART, but 2) they don't have # RUNTIME retention. These minimal keep rules ensure they're not stripped by R8. # TODO(b/215417388): Export this rule from the owning library, core-libart, # via export_proguard_flags_files. -keepclassmembers,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class * { @dalvik.annotation.optimization.** *; } -include proguard_basic_keeps.flags -include proguard/kotlin.flags ================================================ FILE: core/proguard.jacoco.flags ================================================ # Keep everything for the emma classes -keep class com.vladium.** { *; } # Keep everything for the jacoco classes -keep class org.jacoco.** { *; } ================================================ FILE: core/proguard_basic_keeps.flags ================================================ # Preserve line number information for debugging stack traces. -keepattributes SourceFile,LineNumberTable # Annotations are implemented as attributes, so we have to explicitly keep them. # Keep all runtime-visible annotations like RuntimeVisibleParameterAnnotations # and RuntimeVisibleTypeAnnotations, as well as associated defaults. -keepattributes RuntimeVisible*Annotation*,AnnotationDefault # With R8 full mode, certain attributes are only kept when matched with an # explicit keep rule for that target, even with a global -keepattributes rule. # As such, we can add the global keep rule here with minimal cost while # simplifying incremental development. -keepattributes Exceptions # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native -keepclasseswithmembernames,includedescriptorclasses class * { native ; } # class$ methods are inserted by some compilers to implement .class construct, # see http://proguard.sourceforge.net/manual/examples.html#library -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); } # Keep serializable classes and necessary members for serializable classes # Copied from the ProGuard manual at http://proguard.sourceforge.net. -keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient ; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } # Keep all Javascript API methods -keepclassmembers class * { @android.webkit.JavascriptInterface ; } # Keep Throwable's constructor that takes a String argument. -keepclassmembers class * extends java.lang.Throwable { (java.lang.String); } # Please specify classes to be kept explicitly in your package's configuration. # -keep class * extends android.app.Activity # -keep class * extends android.view.View # -keep class * extends android.app.Service # -keep class * extends android.content.BroadcastReceiver # -keep class * extends android.content.ContentProvider # -keep class * extends android.preference.Preference # -keep class * extends android.app.BackupAgent # Parcelable CREATORs must be kept for Parcelable functionality -keepclassmembers class * implements android.os.Parcelable { public static final ** CREATOR; } # The support library contains references to newer platform versions. # Don't warn about those in case this app is linking against an older # platform version. We know about them, and they are safe. # See proguard-android.txt in the SDK package. # # DO NOT USE THIS: We figured it's dangerous to blindly ignore all support library warnings. # ProGuard may strip members of subclass of unknown super classes, in case an app is linking against # LOCAL_SDK_VERSION lower than the support library's LOCAL_SDK_VERSION. # See bug/20658265. # -dontwarn android.support.** # From https://github.com/google/guava/wiki/UsingProGuardWithGuava # Striped64, LittleEndianByteArray, UnsignedBytes, AbstractFuture -dontwarn sun.misc.Unsafe # Futures.getChecked (which often won't work with Proguard anyway) uses this. It # has a fallback, but again, don't use Futures.getChecked on Android regardless. -dontwarn java.lang.ClassValue # Ignore missing annotation references for various support libraries. # While this is not ideal, it should be relatively safe given that # 1) runtime-visible annotations will still be kept, and 2) compile-time # annotations are stripped by R8 anyway. # Note: The ** prefix is used to accommodate jarjar repackaging. # TODO(b/242088131): Remove these exemptions after resolving transitive libs # dependencies that are provided to R8. -dontwarn **android**.annotation*.** -dontwarn **com.google.errorprone.annotations.** -dontwarn javax.annotation.** -dontwarn org.checkerframework.** -dontwarn org.jetbrains.annotations.** # Less spammy. -dontnote # The lite proto runtime uses reflection to access fields based on the names in # the schema, keep all the fields. Wildcard is used to apply the rule to classes # that have been renamed with jarjar. -keepclassmembers class * extends **.protobuf.MessageLite { ; } ================================================ FILE: core/project_definitions.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Allow projects to define their own globally-available variables. # # # Include definitions for prebuilt SDK, if present. # -include prebuilts/sdk/current/definitions.mk ================================================ FILE: core/python_binary_host_mobly_test_config_template.xml ================================================ {EXTRA_CONFIGS} ================================================ FILE: core/python_binary_host_test_config_template.xml ================================================ ================================================ FILE: core/ravenwood_test_config_template.xml ================================================ ================================================ FILE: core/rbe.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Notice: this works only with Google's RBE service. ifneq ($(filter-out false,$(USE_RBE)),) ifdef RBE_DIR rbe_dir := $(RBE_DIR) else rbe_dir := prebuilts/remoteexecution-client/live/ endif ifdef RBE_CXX_POOL cxx_pool := $(RBE_CXX_POOL) else cxx_pool := default endif ifdef RBE_JAVA_POOL java_pool := $(RBE_JAVA_POOL) else java_pool := java16 endif ifdef RBE_CXX_EXEC_STRATEGY cxx_rbe_exec_strategy := $(RBE_CXX_EXEC_STRATEGY) else cxx_rbe_exec_strategy := local endif ifdef RBE_CXX_COMPARE cxx_compare := $(RBE_CXX_COMPARE) else cxx_compare := false endif ifdef RBE_JAVAC_EXEC_STRATEGY javac_exec_strategy := $(RBE_JAVAC_EXEC_STRATEGY) else javac_exec_strategy := remote_local_fallback endif ifdef RBE_R8_EXEC_STRATEGY r8_exec_strategy := $(RBE_R8_EXEC_STRATEGY) else r8_exec_strategy := remote_local_fallback endif ifdef RBE_D8_EXEC_STRATEGY d8_exec_strategy := $(RBE_D8_EXEC_STRATEGY) else d8_exec_strategy := remote_local_fallback endif platform := container-image=docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:1eb7f64b9e17102b970bd7a1af7daaebdb01c3fb777715899ef462d6c6d01a45 cxx_platform := $(platform),Pool=$(cxx_pool) java_r8_d8_platform := $(platform),Pool=$(java_pool) RBE_WRAPPER := $(rbe_dir)/rewrapper RBE_CXX := --labels=type=compile,lang=cpp,compiler=clang --env_var_allowlist=PWD --exec_strategy=$(cxx_rbe_exec_strategy) --platform=$(cxx_platform) --compare=$(cxx_compare) # Append rewrapper to existing *_WRAPPER variables so it's possible to # use both ccache and rewrapper. CC_WRAPPER := $(strip $(CC_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX)) CXX_WRAPPER := $(strip $(CXX_WRAPPER) $(RBE_WRAPPER) $(RBE_CXX)) ifdef RBE_JAVAC JAVAC_WRAPPER := $(strip $(JAVAC_WRAPPER) $(RBE_WRAPPER) --labels=type=compile,lang=java,compiler=javac --exec_strategy=$(javac_exec_strategy) --platform=$(java_r8_d8_platform)) endif ifdef RBE_R8 R8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=r8 --exec_strategy=$(r8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=$(OUT_DIR)/host/linux-x86/framework/r8.jar,build/make/core/proguard_basic_keeps.flags --toolchain_inputs=$(firstword $(JAVA))) endif ifdef RBE_D8 D8_WRAPPER := $(strip $(RBE_WRAPPER) --labels=type=compile,compiler=d8 --exec_strategy=$(d8_exec_strategy) --platform=$(java_r8_d8_platform) --inputs=$(OUT_DIR)/host/linux-x86/framework/d8.jar --toolchain_inputs=$(firstword $(JAVA))) endif rbe_dir := endif ================================================ FILE: core/release_config.mk ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ----------------------------------------------------------------- # Determine which pass this is. # ----------------------------------------------------------------- # On the first pass, we are asked for only PRODUCT_RELEASE_CONFIG_MAPS, # on the second pass, we are asked for whatever else is wanted. _final_product_config_pass:= ifneq (PRODUCT_RELEASE_CONFIG_MAPS,$(DUMP_MANY_VARS)) _final_product_config_pass:=true endif # ----------------------------------------------------------------- # Choose the flag files # ----------------------------------------------------------------- # Release configs are defined in reflease_config_map files, which map # the short name (e.g. -next) used in lunch to the starlark files # defining the build flag values. # # (If you're thinking about aconfig flags, there is one build flag, # RELEASE_ACONFIG_VALUE_SETS, that sets which aconfig_value_set # module to use to set the aconfig flag values.) # # The short release config names *can* appear multiple times, to allow # for AOSP and vendor specific flags under the same name, but the # individual flag values must appear in exactly one config. Vendor # does not override AOSP, or anything like that. This is because # vendor code usually includes prebuilts, and having vendor compile # with different flags from AOSP increases the likelihood of flag # mismatch. # Do this first, because we're going to unset TARGET_RELEASE before # including anyone, so they don't start making conditionals based on it. # This logic is in make because starlark doesn't understand optional # vendor files. # If this is a google source tree, restrict it to only the one file # which has OWNERS control. If it isn't let others define their own. config_map_files := $(wildcard build/release/release_config_map.mk) \ $(wildcard vendor/google_shared/build/release/release_config_map.mk) \ $(if $(wildcard vendor/google/release/release_config_map.mk), \ vendor/google/release/release_config_map.mk, \ $(sort \ $(wildcard device/*/release/release_config_map.mk) \ $(wildcard device/*/*/release/release_config_map.mk) \ $(wildcard vendor/*/release/release_config_map.mk) \ $(wildcard vendor/*/*/release/release_config_map.mk) \ ) \ ) protobuf_map_files := build/release/release_config_map.textproto \ $(wildcard vendor/google_shared/build/release/release_config_map.textproto) \ $(if $(wildcard vendor/google/release/release_config_map.textproto), \ vendor/google/release/release_config_map.textproto, \ $(sort \ $(wildcard device/*/release/release_config_map.textproto) \ $(wildcard device/*/*/release/release_config_map.textproto) \ $(wildcard vendor/*/release/release_config_map.textproto) \ $(wildcard vendor/*/*/release/release_config_map.textproto) \ ) \ ) # Remove support for the legacy approach. _must_protobuf := true # PRODUCT_RELEASE_CONFIG_MAPS is set by Soong using an initial run of product # config to capture only the list of config maps needed by the build. # Keep them in the order provided, but remove duplicates. # Treat .mk and .textproto as equal for duplicate elimination, but force # protobuf if any PRODUCT_RELEASE_CONFIG_MAPS specify .textproto. $(foreach map,$(PRODUCT_RELEASE_CONFIG_MAPS), \ $(if $(filter $(basename $(map)),$(basename $(config_map_files))),, \ $(eval config_map_files += $(map))) \ $(if $(filter $(basename $(map)).textproto,$(map)),$(eval _must_protobuf := true)) \ ) # If we are missing the textproto version of any of $(config_map_files), we cannot use protobuf. _can_protobuf := true $(foreach map,$(config_map_files), \ $(if $(wildcard $(basename $(map)).textproto),,$(eval _can_protobuf :=)) \ ) # If we are missing the mk version of any of $(protobuf_map_files), we must use protobuf. $(foreach map,$(protobuf_map_files), \ $(if $(wildcard $(basename $(map)).mk),,$(eval _must_protobuf := true)) \ ) ifneq (,$(_must_protobuf)) ifeq (,$(_can_protobuf)) # We must use protobuf, but we cannot use protobuf. $(error release config is a mixture of .scl and .textproto) endif endif _use_protobuf := ifneq (,$(_must_protobuf)) _use_protobuf := true else ifneq ($(_can_protobuf),) # Determine the default $(foreach map,$(config_map_files), \ $(if $(wildcard $(dir $(map))/build_config/DEFAULT=proto),$(eval _use_protobuf := true)) \ $(if $(wildcard $(dir $(map))/build_config/DEFAULT=make),$(eval _use_protobuf := )) \ ) # Update for this specific release config only (no inheritance). $(foreach map,$(config_map_files), \ $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=proto),$(eval _use_protobuf := true)) \ $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=make),$(eval _use_protobuf := )) \ ) endif endif ifneq (,$(_use_protobuf)) # The .textproto files are the canonical source of truth. _args := $(foreach map,$(config_map_files), --map $(map) ) ifneq (,$(_must_protobuf)) # Disable the build flag in release-config. _args += --guard=false endif _args += --allow-missing=true ifneq (,$(TARGET_PRODUCT)) _args += --product $(TARGET_PRODUCT) endif _flags_dir:=$(OUT_DIR)/soong/release-config _flags_file:=$(_flags_dir)/release_config-$(TARGET_PRODUCT)-$(TARGET_RELEASE).vars # release-config generates $(_flags_varmk) _flags_varmk:=$(_flags_file:.vars=.varmk) $(shell $(OUT_DIR)/release-config $(_args) >$(OUT_DIR)/release-config.out && touch -t 200001010000 $(_flags_varmk)) $(if $(filter-out 0,$(.SHELLSTATUS)),$(error release-config failed to run)) ifneq (,$(_final_product_config_pass)) # Save the final version of the config. $(shell if ! cmp --quiet $(_flags_varmk) $(_flags_file); then cp $(_flags_varmk) $(_flags_file); fi) # This will also set ALL_RELEASE_CONFIGS_FOR_PRODUCT and _used_files for us. $(eval include $(_flags_file)) $(KATI_extra_file_deps $(OUT_DIR)/release-config $(protobuf_map_files) $(_flags_file)) ifneq (,$(_disallow_lunch_use)) $(error Release config ${TARGET_RELEASE} is disallowed for build. Please use one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT)) endif else # This is the first pass of product config. $(eval include $(_flags_varmk)) endif _used_files := ifeq (,$(_must_protobuf)$(RELEASE_BUILD_FLAGS_IN_PROTOBUF)) _use_protobuf := endif _flags_dir:= _flags_file:= _flags_varmk:= endif ifeq (,$(_use_protobuf)) # The .mk files are the canonical source of truth. # Declare an alias release-config # # This should be used to declare a release as an alias of another, meaning no # release config files should be present. # # $1 config name # $2 release config for which it is an alias define alias-release-config $(call _declare-release-config,$(1),,$(2),true) endef # Declare or extend a release-config. # # The order of processing is: # 1. Recursively apply any overridden release configs. Only apply each config # the first time we reach it. # 2. Apply any files for this release config, in the order they were added to # the declaration. # # Example: # With these declarations: # $(declare-release-config foo, foo.scl) # $(declare-release-config bar, bar.scl, foo) # $(declare-release-config baz, baz.scl, bar) # $(declare-release-config bif, bif.scl, foo baz) # $(declare-release-config bop, bop.scl, bar baz) # # TARGET_RELEASE: # - bar will use: foo.scl bar.scl # - baz will use: foo.scl bar.scl baz.scl # - bif will use: foo.scl bar.scl baz.scl bif.scl # - bop will use: foo.scl bar.scl baz.scl bop.scl # # $1 config name # $2 release config files # $3 overridden release config define declare-release-config $(call _declare-release-config,$(1),$(2),$(3),) endef define _declare-release-config $(if $(strip $(2)$(3)),, \ $(error declare-release-config: config $(strip $(1)) must have release config files, override another release config, or both) \ ) $(if $(strip $(4)),$(eval _all_release_configs.$(strip $(1)).ALIAS := true)) $(eval ALL_RELEASE_CONFIGS_FOR_PRODUCT := $(sort $(ALL_RELEASE_CONFIGS_FOR_PRODUCT) $(strip $(1)))) $(if $(strip $(3)), \ $(if $(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(strip $(3))), $(if $(filter $(_all_release_configs.$(strip $(1)).OVERRIDES),$(strip $(3))),, $(eval _all_release_configs.$(strip $(1)).OVERRIDES := $(_all_release_configs.$(strip $(1)).OVERRIDES) $(strip $(3)))), \ $(error No release config $(strip $(3))) \ ) \ ) $(eval _all_release_configs.$(strip $(1)).DECLARED_IN := $(_included) $(_all_release_configs.$(strip $(1)).DECLARED_IN)) $(eval _all_release_configs.$(strip $(1)).FILES := $(_all_release_configs.$(strip $(1)).FILES) $(strip $(2))) endef # Include the config map files and populate _flag_declaration_files. # If the file is found more than once, only include it the first time. _flag_declaration_files := _included_config_map_files := $(foreach f, $(config_map_files), \ $(eval FLAG_DECLARATION_FILES:= ) \ $(if $(filter $(_included_config_map_files),$(f)),,\ $(eval _included := $(f)) \ $(eval include $(f)) \ $(eval _flag_declaration_files += $(FLAG_DECLARATION_FILES)) \ $(eval _included_config_map_files += $(f)) \ ) \ ) FLAG_DECLARATION_FILES := # Verify that all inherited/overridden release configs are declared. $(foreach config,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\ $(foreach r,$(all_release_configs.$(r).OVERRIDES),\ $(if $(strip $(_all_release_configs.$(r).FILES)$(_all_release_configs.$(r).OVERRIDES)),,\ $(error Release config $(config) [declared in: $(_all_release_configs.$(r).DECLARED_IN)] inherits from non-existent $(r).)\ ))) # Verify that alias configs do not have config files. $(foreach r,$(ALL_RELEASE_CONFIGS_FOR_PRODUCT),\ $(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\ $(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\ ))) # Use makefiles endif ifeq ($(TARGET_RELEASE),) # We allow some internal paths to explicitly set TARGET_RELEASE to the # empty string. For the most part, 'make' treats unset and empty string as # the same. But the following line differentiates, and will only assign # if the variable was completely unset. TARGET_RELEASE ?= was_unset ifeq ($(TARGET_RELEASE),was_unset) $(error No release config set for target; please set TARGET_RELEASE, or if building on the command line use 'lunch --', where release is one of: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT)) endif # Instead of leaving this string empty, we want to default to a valid # setting. Full builds coming through this path is a bug, but in case # of such a bug, we want to at least get consistent, valid results. TARGET_RELEASE = trunk_staging endif # During pass 1 of product config, using a non-existent release config is not an error. # We can safely assume that we are doing pass 1 if DUMP_MANY_VARS=="PRODUCT_RELEASE_CONFIG_MAPS". ifneq (,$(_final_product_config_pass)) ifeq ($(filter $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), $(TARGET_RELEASE)),) $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(ALL_RELEASE_CONFIGS_FOR_PRODUCT)) endif endif ifeq (,$(_use_protobuf)) # Choose flag files # Don't sort this, use it in the order they gave us. # Do allow duplicate entries, retaining only the first usage. flag_value_files := # Apply overrides recursively # # $1 release config that we override applied_releases := define _apply-release-config-overrides $(foreach r,$(1), \ $(if $(filter $(r),$(applied_releases)),, \ $(foreach o,$(_all_release_configs.$(r).OVERRIDES),$(call _apply-release-config-overrides,$(o)))\ $(eval applied_releases += $(r))\ $(foreach f,$(_all_release_configs.$(r).FILES), \ $(if $(filter $(f),$(flag_value_files)),,$(eval flag_value_files += $(f)))\ )\ )\ ) endef $(call _apply-release-config-overrides,$(TARGET_RELEASE)) # Unset variables so they can't use them define declare-release-config $(error declare-release-config can only be called from inside release_config_map.mk files) endef define _apply-release-config-overrides $(error invalid use of apply-release-config-overrides) endef # use makefiles endif # TODO: Remove this check after enough people have sourced lunch that we don't # need to worry about it trying to do get_build_vars TARGET_RELEASE. Maybe after ~9/2023 ifneq ($(CALLED_FROM_SETUP),true) define TARGET_RELEASE $(error TARGET_RELEASE may not be accessed directly. Use individual flags.) endef else TARGET_RELEASE:= endif .KATI_READONLY := TARGET_RELEASE ifeq (,$(_use_protobuf)) $(foreach config, $(ALL_RELEASE_CONFIGS_FOR_PRODUCT), \ $(eval _all_release_configs.$(config).DECLARED_IN:= ) \ $(eval _all_release_configs.$(config).FILES:= ) \ ) applied_releases:= # use makefiles endif config_map_files:= protobuf_map_files:= ifeq (,$(_use_protobuf)) # ----------------------------------------------------------------- # Flag declarations and values # ----------------------------------------------------------------- # This part is in starlark. We generate a root starlark file that loads # all of the flags declaration files that we found, and the flag_value_files # that we chose from the config map above. Then we run that, and load the # results of that into the make environment. # _flag_declaration_files is the combined list of FLAG_DECLARATION_FILES set by # release_config_map.mk files above. # Because starlark can't find files with $(wildcard), write an entrypoint starlark script that # contains the result of the above wildcards for the starlark code to use. filename_to_starlark=$(subst /,_,$(subst .,_,$(1))) _c:=load("//build/make/core/release_config.scl", "release_config") _c+=$(newline)def add(d, k, v): _c+=$(newline)$(space)d = dict(d) _c+=$(newline)$(space)d[k] = v _c+=$(newline)$(space)return d _c+=$(foreach f,$(_flag_declaration_files),$(newline)load("$(f)", flags_$(call filename_to_starlark,$(f)) = "flags")) _c+=$(newline)all_flags = [] $(foreach f,$(_flag_declaration_files),+ [add(x, "declared_in", "$(f)") for x in flags_$(call filename_to_starlark,$(f))]) _c+=$(foreach f,$(flag_value_files),$(newline)load("//$(f)", values_$(call filename_to_starlark,$(f)) = "values")) _c+=$(newline)all_values = [] $(foreach f,$(flag_value_files),+ [add(x, "set_in", "$(f)") for x in values_$(call filename_to_starlark,$(f))]) _c+=$(newline)variables_to_export_to_make = release_config(all_flags, all_values) $(file >$(OUT_DIR)/release_config_entrypoint.scl,$(_c)) _c:= filename_to_starlark:= # Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't # rerun kati every build. Kati will replay the $(file) command that generates it every build, # updating its timestamp. # # We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something # outside of the source tree. $(call run-starlark,$(OUT_DIR)/release_config_entrypoint.scl,$(OUT_DIR)/release_config_entrypoint.scl,--allow_external_entrypoint) # use makefiles endif _can_protobuf := _must_protobuf := _use_protobuf := ================================================ FILE: core/release_config.scl ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Export build flags (with values) to make. """ load("//build/bazel/utils:schema_validation.scl", "validate") # Partitions that get build system flag summaries _flag_partitions = [ "product", "system", "system_ext", "vendor", ] ALL = ["all"] PRODUCT = ["product"] SYSTEM = ["system"] SYSTEM_EXT = ["system_ext"] VENDOR = ["vendor"] _valid_types = ["NoneType", "bool", "list", "string", "int"] _all_flags_schema = { "type": "list", "of": { "type": "dict", "required_keys": { "name": {"type": "string"}, "partitions": { "type": "list", "of": { "type": "string", "choices": _flag_partitions + ["all"], }, "unique": True, }, "default": { "or": [ {"type": t} for t in _valid_types ], }, "origin": {"type": "string"}, "declared_in": {"type": "string"}, }, "optional_keys": { "appends": { "type": "bool", }, }, }, } _all_values_schema = { "type": "list", "of": { "type": "dict", "required_keys": { "name": {"type": "string"}, "value": { "or": [ {"type": t} for t in _valid_types ], }, "set_in": {"type": "string"}, }, }, } def flag(name, partitions, default, *, origin = "Unknown", appends = False): """Declare a flag. Args: name: name of the flag partitions: the partitions where this should be recorded. default: the default value of the flag. origin: The origin of this flag. appends: Whether new values should be append (not replace) the old. Returns: A dictionary containing the flag declaration. """ if not partitions: fail("At least 1 partition is required") if not name.startswith("RELEASE_"): fail("Release flag names must start with RELEASE_") if " " in name or "\t" in name or "\n" in name: fail("Flag names must not contain whitespace: \"" + name + "\"") for partition in partitions: if partition == "all": if len(partitions) > 1: fail("\"all\" can't be combined with other partitions: " + str(partitions)) elif partition not in _flag_partitions: fail("Invalid partition: " + partition + ", allowed partitions: " + str(_flag_partitions)) if type(default) not in _valid_types: fail("Invalid type of default for flag \"" + name + "\" (" + type(default) + ")") return { "name": name, "partitions": partitions, "default": default, "appends": appends, "origin": origin, } def value(name, value): """Define the flag value for a particular configuration. Args: name: The name of the flag. value: The value for the flag. Returns: A dictionary containing the name and value to be used. """ return { "name": name, "value": value, } def _format_value(val): """Format the starlark type correctly for make. Args: val: The value to format Returns: The value, formatted correctly for make. """ if type(val) == "NoneType": return "" elif type(val) == "bool": return "true" if val else "" else: return val def equal_flag_declaration(flag, other): """Return true if the flag declarations are equal. Args: flag: This flag declaration. other: Another flag declaration. Returns: Whether the declarations are the same. """ for key in "name", "partitions", "default", "appends": if flag[key] != other[key]: return False # For now, allow Unknown to match any other origin. if flag["origin"] == "Unknown" or other["origin"] == "Unknown": return True return flag["origin"] == other["origin"] def release_config(all_flags, all_values): """Return the make variables that should be set for this release config. Args: all_flags: A list of flag objects (from flag() calls). all_values: A list of value objects (from value() calls). Returns: A dictionary of {name: value} variables for make. """ validate(all_flags, _all_flags_schema) validate(all_values, _all_values_schema) # Final values. values = {} # Validate flags flag_names = [] flags_dict = {} for flag in all_flags: name = flag["name"] if name in flag_names: if equal_flag_declaration(flag, flags_dict[name]): continue else: fail(flag["declared_in"] + ": Duplicate declaration of flag " + name + " (declared first in " + flags_dict[name]["declared_in"] + ")") flag_names.append(name) flags_dict[name] = flag # Set the flag value to the default value. values[name] = {"name": name, "value": _format_value(flag["default"]), "set_in": flag["declared_in"]} # Record which flags go on which partition partitions = {} for flag in all_flags: for partition in flag["partitions"]: if partition == "all": if len(flag["partitions"]) > 1: fail("\"all\" can't be combined with other partitions: " + str(flag["partitions"])) for partition in _flag_partitions: partitions.setdefault(partition, []).append(flag["name"]) else: partitions.setdefault(partition, []).append(flag["name"]) # Generate final values. # Only declared flags may have a value. for value in all_values: name = value["name"] if name not in flag_names: fail(value["set_in"] + ": Value set for undeclared build flag: " + name) if flags_dict[name]["appends"]: if name in values: values[name]["value"] += " " + value["value"] values[name]["set_in"] += " " + value["set_in"] else: values[name] = value else: values[name] = value # Collect values result = { "_ALL_RELEASE_FLAGS": sorted(flag_names), } for partition, names in partitions.items(): result["_ALL_RELEASE_FLAGS.PARTITIONS." + partition] = names for flag in all_flags: val = _format_value(values[flag["name"]]["value"]) result[flag["name"]] = val result["_ALL_RELEASE_FLAGS." + flag["name"] + ".PARTITIONS"] = flag["partitions"] result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DEFAULT"] = _format_value(flag["default"]) result["_ALL_RELEASE_FLAGS." + flag["name"] + ".VALUE"] = val result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DECLARED_IN"] = flag["declared_in"] result["_ALL_RELEASE_FLAGS." + flag["name"] + ".SET_IN"] = values[flag["name"]]["set_in"] result["_ALL_RELEASE_FLAGS." + flag["name"] + ".ORIGIN"] = flag["origin"] return result ================================================ FILE: core/robolectric_test_config_template.xml ================================================ ================================================ FILE: core/root.mk ================================================ ### DO NOT EDIT THIS FILE ### include build/make/core/main.mk ### DO NOT EDIT THIS FILE ### ================================================ FILE: core/rust_device_benchmark_config_template.xml ================================================ ================================================ FILE: core/rust_device_test_config_template.xml ================================================ {EXTRA_CONFIGS} ================================================ FILE: core/rust_host_benchmark_config_template.xml ================================================ ================================================ FILE: core/rust_host_test_config_template.xml ================================================ ================================================ FILE: core/sbom.mk ================================================ # For SBOM generation # This is included by base_rules.mk and is not necessary to be included in other .mk files # unless a .mk file changes its installed file after including base_rules.mk. ifdef my_register_name # ALL_INSTALLED_FILES.$(installed_file).STATIC_LIBRARIES: list of module name of static libraries, e.g. libc++demangle libclang_rt.builtins, for primary arch # ALL_INSTALLED_FILES.$(installed_file).WHOLE_STATIC_LIBRARIES: list of module name of static libraries, e.g. libc++demangle_32 libclang_rt.builtins_32, for 2nd arch. ifneq (, $(strip $(ALL_MODULES.$(my_register_name).INSTALLED))) $(foreach installed_file,$(ALL_MODULES.$(my_register_name).INSTALLED),\ $(eval ALL_INSTALLED_FILES.$(installed_file) := $(my_register_name))\ $(eval ALL_INSTALLED_FILES.$(installed_file).STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_STATIC_LIBRARIES))),$l$(if $(LOCAL_2ND_ARCH_VAR_PREFIX),$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))))\ $(eval ALL_INSTALLED_FILES.$(installed_file).WHOLE_STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_WHOLE_STATIC_LIBRARIES))),$l$(if $(LOCAL_2ND_ARCH_VAR_PREFIX),$($(my_prefix)2ND_ARCH_MODULE_SUFFIX))))\ ) endif ifeq (STATIC_LIBRARIES,$(LOCAL_MODULE_CLASS)) ALL_STATIC_LIBRARIES.$(my_register_name).STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_STATIC_LIBRARIES))),$l$($(my_prefix)2ND_ARCH_MODULE_SUFFIX)) ALL_STATIC_LIBRARIES.$(my_register_name).WHOLE_STATIC_LIBRARIES := $(foreach l,$(strip $(sort $(LOCAL_WHOLE_STATIC_LIBRARIES))),$l$($(my_prefix)2ND_ARCH_MODULE_SUFFIX)) ifdef LOCAL_SOONG_MODULE_TYPE ALL_STATIC_LIBRARIES.$(my_register_name).BUILT_FILE := $(LOCAL_PREBUILT_MODULE_FILE) endif endif endif ================================================ FILE: core/sdk_check.mk ================================================ # Enforcement checks that LOCAL_SDK_VERSION and LOCAL_PRIVATE_PLATFORM_APIS are # set correctly. # Should be included by java targets that allow specifying LOCAL_SDK_VERSION. # The JAVA_SDK_ENFORCEMENT_WARNING and JAVA_SDK_ENFORCEMENT_ERROR variables may # be set to a particular module class to enable warnings and errors for that # subtype. allowed_modules := framework-res__auto_generated_rro ifeq (,$(JAVA_SDK_ENFORCEMENT_ERROR)) JAVA_SDK_ENFORCEMENT_ERROR := APPS endif ifeq ($(LOCAL_SDK_VERSION)$(LOCAL_PRIVATE_PLATFORM_APIS),) ifeq (,$(filter $(LOCAL_MODULE),$(allowed_modules))) ifneq ($(JAVA_SDK_ENFORCEMENT_WARNING)$(JAVA_SDK_ENFORCEMENT_ERROR),) my_message := Must specify LOCAL_SDK_VERSION or LOCAL_PRIVATE_PLATFORM_APIS, ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_ERROR)) $(call pretty-error,$(my_message)) endif ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_WARNING)) $(call pretty-warning,$(my_message)) endif my_message := endif endif else ifneq ($(LOCAL_SDK_VERSION),) ifneq ($(LOCAL_PRIVATE_PLATFORM_APIS),) my_message := Specifies both LOCAL_SDK_VERSION ($(LOCAL_SDK_VERSION)) and my_message += LOCAL_PRIVATE_PLATFORM_APIS ($(LOCAL_PRIVATE_PLATFORM_APIS)) my_message += but should specify only one $(call pretty-error,$(my_message)) my_message := endif endif ================================================ FILE: core/shared_library.mk ================================================ $(call record-module-type,SHARED_LIBRARY) ifdef LOCAL_IS_HOST_MODULE $(call pretty-error,BUILD_SHARED_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_SHARED_LIBRARY instead.) endif my_prefix := TARGET_ include $(BUILD_SYSTEM)/multilib.mk ifndef my_module_multilib # libraries default to building for both architecturess my_module_multilib := both endif ifeq ($(my_module_multilib),both) ifneq ($(LOCAL_MODULE_PATH),) ifneq ($(TARGET_2ND_ARCH),) $(error $(LOCAL_MODULE): LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds, use LOCAL_MODULE_RELATIVE_PATH instead) endif endif ifneq ($(LOCAL_UNSTRIPPED_PATH),) ifneq ($(TARGET_2ND_ARCH),) $(error $(LOCAL_MODULE): LOCAL_UNSTRIPPED_PATH for shared libraries is unsupported in multiarch builds) endif endif endif # my_module_multilib == both LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/shared_library_internal.mk endif ifdef TARGET_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for TARGET_2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/shared_library_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # TARGET_2ND_ARCH my_module_arch_supported := ########################################################### ## Copy headers to the install tree ########################################################### ifdef LOCAL_COPY_HEADERS $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) include $(BUILD_SYSTEM)/copy_headers.mk endif ================================================ FILE: core/shared_library_internal.mk ================================================ ########################################################### ## Standard rules for building a normal shared library. ## ## Additional inputs from base_rules.make: ## None. ## ## LOCAL_MODULE_SUFFIX will be set for you. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := SHARED_LIBRARIES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX) endif ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)$(LOCAL_MODULE_STEM_32)$(LOCAL_MODULE_STEM_64)),) $(error $(LOCAL_PATH): Cannot set module stem for a library) endif ifdef target-shared-library-hook $(call target-shared-library-hook) endif skip_build_from_source := ifdef LOCAL_PREBUILT_MODULE_FILE ifeq (,$(call if-build-from-source,$(LOCAL_MODULE),$(LOCAL_PATH))) include $(BUILD_SYSTEM)/prebuilt_internal.mk skip_build_from_source := true endif endif ifndef skip_build_from_source include $(BUILD_SYSTEM)/dynamic_binary.mk # Define PRIVATE_ variables from global vars ifeq ($(LOCAL_NO_LIBCRT_BUILTINS),true) my_target_libcrt_builtins := else my_target_libcrt_builtins := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBCRT_BUILTINS) endif ifeq ($(LOCAL_NO_CRT),true) my_target_crtbegin_so_o := my_target_crtend_so_o := else ifeq ($(call module-in-vendor-or-product),true) my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.vendor) my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.vendor) else my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so) my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so) endif ifneq ($(LOCAL_SDK_VERSION),) my_target_crtbegin_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtbegin_so.sdk.$(my_ndk_crt_version)) my_target_crtend_so_o := $(SOONG_$(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJECT_crtend_so.sdk.$(my_ndk_crt_version)) endif $(linked_module): PRIVATE_TARGET_LIBCRT_BUILTINS := $(my_target_libcrt_builtins) $(linked_module): PRIVATE_TARGET_CRTBEGIN_SO_O := $(my_target_crtbegin_so_o) $(linked_module): PRIVATE_TARGET_CRTEND_SO_O := $(my_target_crtend_so_o) $(linked_module): \ $(all_objects) \ $(all_libraries) \ $(my_target_crtbegin_so_o) \ $(my_target_crtend_so_o) \ $(my_target_libcrt_builtins) \ $(LOCAL_ADDITIONAL_DEPENDENCIES) $(CLANG_CXX) $(transform-o-to-shared-lib) ifeq ($(my_native_coverage),true) gcno_suffix := .zip built_whole_gcno_libraries := \ $(foreach lib,$(my_whole_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ $(my_host_cross))/$(lib)$(gcno_suffix)) built_static_gcno_libraries := \ $(foreach lib,$(my_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ $(my_host_cross))/$(lib)$(gcno_suffix)) ifdef LOCAL_IS_HOST_MODULE my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) else my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) endif GCNO_ARCHIVE := $(basename $(my_installed_module_stem))$(gcno_suffix) $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(strip $(built_static_gcno_libraries)) $(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(built_static_gcno_libraries) $(package-coverage-files) $(my_coverage_path)/$(GCNO_ARCHIVE) : $(intermediates)/$(GCNO_ARCHIVE) $(copy-file-to-target) $(LOCAL_BUILT_MODULE): $(my_coverage_path)/$(GCNO_ARCHIVE) endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=SHARED_LIBRARY)) endif # skip_build_from_source ================================================ FILE: core/shell_test_config_template.xml ================================================ ================================================ FILE: core/soong_android_app_set.mk ================================================ # App prebuilt coming from Soong. # Extra inputs: # LOCAL_APK_SET_INSTALL_FILE ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,soong_apk_set.mk may only be used from Soong) endif LOCAL_BUILT_MODULE_STEM := package.apk LOCAL_INSTALLED_MODULE_STEM := $(notdir $(LOCAL_PREBUILT_MODULE_FILE)) ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) PACKAGES := $(PACKAGES) $(LOCAL_MODULE) # We can't know exactly what apk files would be outputted yet. # Let extract_apks generate apkcerts.txt and merge it later. PACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE) SOONG_ALREADY_CONV += $(LOCAL_MODULE) ================================================ FILE: core/soong_app_prebuilt.mk ================================================ # App prebuilt coming from Soong. # Extra inputs: # LOCAL_SOONG_BUILT_INSTALLED # LOCAL_SOONG_BUNDLE # LOCAL_SOONG_CLASSES_JAR # LOCAL_SOONG_DEX_JAR # LOCAL_SOONG_HEADER_JAR # LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR # LOCAL_SOONG_PROGUARD_DICT # LOCAL_SOONG_PROGUARD_USAGE_ZIP # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE # LOCAL_SOONG_RRO_DIRS # LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH) # LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH) # LOCAL_SOONG_JNI_LIBS_SYMBOLS # LOCAL_SOONG_DEXPREOPT_CONFIG ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,soong_app_prebuilt.mk may only be used from Soong) endif LOCAL_MODULE_SUFFIX := .apk LOCAL_BUILT_MODULE_STEM := package.apk intermediates.COMMON := $(call local-intermediates-dir,COMMON) full_classes_jar := $(intermediates.COMMON)/classes.jar full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### ifdef LOCAL_SOONG_CLASSES_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar))) $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar))) $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar))) ifneq ($(TURBINE_ENABLED),false) ifdef LOCAL_SOONG_HEADER_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar))) else $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar))) endif endif # TURBINE_ENABLED != false javac-check : $(full_classes_jar) javac-check-$(LOCAL_MODULE) : $(full_classes_jar) .PHONY: javac-check-$(LOCAL_MODULE) endif ifdef LOCAL_SOONG_DEXPREOPT_CONFIG my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config))) $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config) endif # Run veridex on product, system_ext and vendor modules. # We skip it for unbundled app builds where we cannot build veridex. module_run_appcompat := ifeq (true,$(non_system_module)) ifeq (,$(TARGET_BUILD_APPS)) # not unbundled app build ifeq (,$(filter sdk,$(MAKECMDGOALS))) # not sdk build (which is another form of unbundled build) ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) module_run_appcompat := true endif endif endif endif ifeq ($(module_run_appcompat),true) $(LOCAL_BUILT_MODULE): $(appcompat-files) $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE) $(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE) @echo "Copy: $@" $(copy-file-to-target) $(appcompat-header) $(run-appcompat) else $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) endif ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) $(call add-dependency,$(LOCAL_BUILT_MODULE),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) endif ifdef LOCAL_SOONG_PROGUARD_DICT $(eval $(call copy-r8-dictionary-file-with-mapping,\ $(LOCAL_SOONG_PROGUARD_DICT),\ $(intermediates.COMMON)/proguard_dictionary,\ $(intermediates.COMMON)/proguard_dictionary.textproto)) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \ $(intermediates.COMMON)/proguard_dictionary \ $(LOCAL_SOONG_CLASSES_JAR) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \ -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \ -f $(intermediates.COMMON)/proguard_dictionary \ -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \ -f $(LOCAL_SOONG_CLASSES_JAR) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto endif ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP) endif ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE resource_export_package := $(intermediates.COMMON)/package-export.apk resource_export_stamp := $(intermediates.COMMON)/src/R.stamp $(resource_export_package): PRIVATE_STAMP := $(resource_export_stamp) $(resource_export_package): .KATI_IMPLICIT_OUTPUTS := $(resource_export_stamp) $(resource_export_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) @echo "Copy: $@" $(copy-file-to-target) touch $(PRIVATE_STAMP) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(resource_export_package)) endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE java-dex: $(LOCAL_SOONG_DEX_JAR) # Copy test suite files. ifdef LOCAL_COMPATIBILITY_SUITE my_apks_to_install := $(foreach f,$(filter %.apk %.idsig,$(LOCAL_SOONG_BUILT_INSTALLED)),$(call word-colon,1,$(f))) $(foreach suite, $(LOCAL_COMPATIBILITY_SUITE), \ $(eval my_compat_dist_$(suite) := $(foreach dir, $(call compatibility_suite_dirs,$(suite)), \ $(foreach a,$(my_apks_to_install),\ $(call compat-copy-pair,$(a),$(dir)/$(notdir $(a))))))) $(call create-suite-dependencies) endif # install symbol files of JNI libraries my_jni_lib_symbols_copy_files := $(foreach f,$(LOCAL_SOONG_JNI_LIBS_SYMBOLS),\ $(call word-colon,1,$(f)):$(patsubst $(PRODUCT_OUT)/%,$(TARGET_OUT_UNSTRIPPED)/%,$(call word-colon,2,$(f)))) $(foreach f, $(my_jni_lib_symbols_copy_files), \ $(eval $(call copy-unstripped-elf-file-with-mapping, \ $(call word-colon,1,$(f)), \ $(call word-colon,2,$(f)), \ $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(call word-colon,2,$(f)).textproto)\ ))\ ) symbolic_outputs := $(foreach f,$(my_jni_lib_symbols_copy_files),$(call word-colon,2,$(f))) symbolic_mappings := $(foreach f,$(symbolic_outputs),$(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(f).textproto)) ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_outputs) ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(symbolic_mappings) $(LOCAL_BUILT_MODULE): | $(symbolic_outputs) # embedded JNI will already have been handled by soong my_embed_jni := my_prebuilt_jni_libs := ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE)) ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH) my_2nd_arch_prefix := LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_ARCH)) partition_lib_pairs := $(LOCAL_SOONG_JNI_LIBS_PARTITION_$(TARGET_ARCH)) include $(BUILD_SYSTEM)/install_jni_libs_internal.mk endif ifdef TARGET_2ND_ARCH ifdef LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH) my_2nd_arch_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX) LOCAL_JNI_SHARED_LIBRARIES := $(LOCAL_SOONG_JNI_LIBS_$(TARGET_2ND_ARCH)) partition_lib_pairs := $(LOCAL_SOONG_JNI_LIBS_PARTITION_$(TARGET_2ND_ARCH)) include $(BUILD_SYSTEM)/install_jni_libs_internal.mk endif endif endif LOCAL_SHARED_JNI_LIBRARIES := my_embed_jni := my_prebuilt_jni_libs := my_2nd_arch_prefix := partition_lib_pairs := PACKAGES := $(PACKAGES) $(LOCAL_MODULE) ifndef LOCAL_CERTIFICATE $(call pretty-error,LOCAL_CERTIFICATE must be set for soong_app_prebuilt.mk) endif ifeq ($(LOCAL_CERTIFICATE),PRESIGNED) # The magic string "PRESIGNED" means this package is already checked # signed with its release key. # # By setting .CERTIFICATE but not .PRIVATE_KEY, this package will be # mentioned in apkcerts.txt (with certificate set to "PRESIGNED") # but the dexpreopt process will not try to re-sign the app. PACKAGES.$(LOCAL_MODULE).CERTIFICATE := PRESIGNED else ifneq ($(LOCAL_CERTIFICATE),) PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE) PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(patsubst %.x509.pem,%.pk8,$(LOCAL_CERTIFICATE)) endif include $(BUILD_SYSTEM)/app_certificate_validate.mk PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) ifneq ($(LOCAL_MODULE_STEM),) PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE_STEM) else PACKAGES.$(LOCAL_MODULE).STEM := $(LOCAL_MODULE) endif # Set a actual_partition_tag (calculated in base_rules.mk) for the package. PACKAGES.$(LOCAL_MODULE).PARTITION := $(actual_partition_tag) ifdef LOCAL_SOONG_BUNDLE ALL_MODULES.$(my_register_name).BUNDLE := $(LOCAL_SOONG_BUNDLE) endif ifdef LOCAL_SOONG_LINT_REPORTS ALL_MODULES.$(my_register_name).LINT_REPORTS := $(LOCAL_SOONG_LINT_REPORTS) endif ifndef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_SDK_VERSION),system_current) my_link_type := java:system else ifneq ($(LOCAL_SDK_VERSION),) my_link_type := java:sdk else my_link_type := java:platform endif # warn/allowed types are both empty because Soong modules can't depend on # make-defined modules. my_warn_types := my_allowed_types := my_link_deps := my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := COMMON include $(BUILD_SYSTEM)/link_type.mk endif # !LOCAL_IS_HOST_MODULE ifdef LOCAL_PREBUILT_COVERAGE_ARCHIVE my_coverage_dir := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) my_coverage_copy_pairs := $(foreach f,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(f):$(my_coverage_dir)/$(notdir $(f))) my_coverage_files := $(call copy-many-files,$(my_coverage_copy_pairs)) $(LOCAL_INSTALLED_MODULE): $(my_coverage_files) endif SOONG_ALREADY_CONV += $(LOCAL_MODULE) ########################################################### ## SBOM generation ########################################################### include $(BUILD_SBOM_GEN) ================================================ FILE: core/soong_cc_rust_prebuilt.mk ================================================ # Native prebuilt coming from Soong. # Extra inputs: # LOCAL_SOONG_LINK_TYPE # LOCAL_SOONG_TOC # LOCAL_SOONG_UNSTRIPPED_BINARY # LOCAL_SOONG_VNDK_VERSION : means the version of VNDK where this module belongs ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,soong_cc_rust_prebuilt.mk may only be used from Soong) endif ifdef LOCAL_IS_HOST_MODULE ifneq ($(HOST_OS),$(LOCAL_MODULE_HOST_OS)) my_prefix := HOST_CROSS_ LOCAL_HOST_PREFIX := $(my_prefix) else my_prefix := HOST_ LOCAL_HOST_PREFIX := endif else my_prefix := TARGET_ endif ifeq ($($(my_prefix)ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH)) # primary arch LOCAL_2ND_ARCH_VAR_PREFIX := else ifeq ($($(my_prefix)2ND_ARCH),$(LOCAL_MODULE_$(my_prefix)ARCH)) # secondary arch LOCAL_2ND_ARCH_VAR_PREFIX := $($(my_prefix)2ND_ARCH_VAR_PREFIX) else $(call pretty-error,Unsupported LOCAL_MODULE_$(my_prefix)ARCH=$(LOCAL_MODULE_$(my_prefix)ARCH)) endif # Don't install static/rlib/proc_macro libraries. ifndef LOCAL_UNINSTALLABLE_MODULE ifneq ($(filter STATIC_LIBRARIES RLIB_LIBRARIES PROC_MACRO_LIBRARIES,$(LOCAL_MODULE_CLASS)),) LOCAL_UNINSTALLABLE_MODULE := true endif endif my_check_same_vndk_variants := same_vndk_variants_stamp := ifeq ($(LOCAL_CHECK_SAME_VNDK_VARIANTS),true) ifeq ($(filter hwaddress address, $(SANITIZE_TARGET)),) ifneq ($(CLANG_COVERAGE),true) # Do not compare VNDK variant for special cases e.g. coverage builds. ifneq ($(SKIP_VNDK_VARIANTS_CHECK),true) my_check_same_vndk_variants := true same_vndk_variants_stamp := $(call local-intermediates-dir,,$(LOCAL_2ND_ARCH_VAR_PREFIX))/same_vndk_variants.timestamp endif endif endif endif ifeq ($(my_check_same_vndk_variants),true) # Add the timestamp to the CHECKED list so that `checkbuild` can run it. # Note that because `checkbuild` doesn't check LOCAL_BUILT_MODULE for soong-built modules adding # the timestamp to LOCAL_BUILT_MODULE isn't enough. It is skipped when the vendor variant # isn't used at all and it may break in the downstream trees. LOCAL_ADDITIONAL_CHECKED_MODULE += $(same_vndk_variants_stamp) endif ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES RLIB_LIBRARIES DYLIB_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),) # Soong module is a static or shared library EXPORTS_LIST += $(intermediates) EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS) EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS) ifdef LOCAL_SOONG_TOC $(eval $(call copy-one-file,$(LOCAL_SOONG_TOC),$(LOCAL_BUILT_MODULE).toc)) $(call add-dependency,$(LOCAL_BUILT_MODULE).toc,$(LOCAL_BUILT_MODULE)) $(my_all_targets): $(LOCAL_BUILT_MODULE).toc endif SOONG_ALREADY_CONV += $(LOCAL_MODULE) my_link_type := $(LOCAL_SOONG_LINK_TYPE) my_warn_types := my_allowed_types := my_link_deps := my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := include $(BUILD_SYSTEM)/link_type.mk endif ifeq ($(call module-in-vendor-or-product),true) ifneq ($(LOCAL_VNDK_DEPEND_ON_CORE_VARIANT),true) name_without_suffix := $(patsubst %.vendor,%,$(LOCAL_MODULE)) ifneq ($(name_without_suffix),$(LOCAL_MODULE)) SPLIT_VENDOR.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1 else name_without_suffix := $(patsubst %.product,%,$(LOCAL_MODULE)) ifneq ($(name_without_suffix),$(LOCAL_MODULE)) SPLIT_PRODUCT.$(LOCAL_MODULE_CLASS).$(name_without_suffix) := 1 endif endif name_without_suffix := endif endif # Check prebuilt ELF binaries. ifdef LOCAL_INSTALLED_MODULE ifneq ($(LOCAL_CHECK_ELF_FILES),) my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE) my_system_shared_libraries := $(LOCAL_SYSTEM_SHARED_LIBRARIES) include $(BUILD_SYSTEM)/check_elf_file.mk endif endif # The real dependency will be added after all Android.mks are loaded and the install paths # of the shared libraries are determined. ifdef LOCAL_INSTALLED_MODULE ifdef LOCAL_SHARED_LIBRARIES my_shared_libraries := $(LOCAL_SHARED_LIBRARIES) ifeq ($(call module-in-vendor-or-product),true) ifdef LOCAL_IN_PRODUCT my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) else my_shared_libraries := $(foreach l,$(my_shared_libraries),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) endif endif $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_shared_libraries)) endif ifdef LOCAL_DYLIB_LIBRARIES my_dylibs := $(LOCAL_DYLIB_LIBRARIES) # Treat these as shared library dependencies for installation purposes. ifeq ($(call module-in-vendor-or-product),true) ifdef LOCAL_IN_PRODUCT my_dylibs := $(foreach l,$(my_dylibs),\ $(if $(SPLIT_PRODUCT.SHARED_LIBRARIES.$(l)),$(l).product,$(l))) else my_dylibs := $(foreach l,$(my_dylibs),\ $(if $(SPLIT_VENDOR.SHARED_LIBRARIES.$(l)),$(l).vendor,$(l))) endif endif $(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)DEPENDENCIES_ON_SHARED_LIBRARIES += \ $(my_register_name):$(LOCAL_INSTALLED_MODULE):$(subst $(space),$(comma),$(my_dylibs)) endif endif ifeq ($(my_check_same_vndk_variants),true) my_core_register_name := $(subst .vendor,,$(subst .product,,$(my_register_name))) my_core_variant_files := $(call module-target-built-files,$(my_core_register_name)) my_core_shared_lib := $(sort $(filter %.so,$(my_core_variant_files))) $(same_vndk_variants_stamp): PRIVATE_CORE_VARIANT := $(my_core_shared_lib) $(same_vndk_variants_stamp): PRIVATE_VENDOR_VARIANT := $(LOCAL_PREBUILT_MODULE_FILE) $(same_vndk_variants_stamp): PRIVATE_TOOLS_PREFIX := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)TOOLS_PREFIX) $(same_vndk_variants_stamp): $(my_core_shared_lib) $(LOCAL_PREBUILT_MODULE_FILE) $(call verify-vndk-libs-identical,\ $(PRIVATE_CORE_VARIANT),\ $(PRIVATE_VENDOR_VARIANT),\ $(PRIVATE_TOOLS_PREFIX)) touch $@ $(LOCAL_BUILT_MODULE): $(same_vndk_variants_stamp) endif # Use copy-or-link-prebuilt-to-target for host executables and shared libraries, # to preserve symlinks to the source trees. They can then run directly from the # prebuilt directories where the linker can load their dependencies using # relative RUNPATHs. $(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE) ifeq ($(LOCAL_IS_HOST_MODULE) $(if $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),true,),true true) $(copy-or-link-prebuilt-to-target) ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) [ -x $@ ] || ( $(call echo-error,$@,Target of symlink is not executable); false ) endif else $(transform-prebuilt-to-target) ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),) $(hide) chmod +x $@ endif endif ifndef LOCAL_IS_HOST_MODULE ifdef LOCAL_SOONG_UNSTRIPPED_BINARY ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) my_symbol_path := $(if $(LOCAL_SOONG_SYMBOL_PATH),$(LOCAL_SOONG_SYMBOL_PATH),$(my_module_path)) # Store a copy with symbols for symbolic debugging my_unstripped_path := $(TARGET_OUT_UNSTRIPPED)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path)) # drop /root as /root is mounted as / my_unstripped_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/root/%,$(TARGET_OUT_UNSTRIPPED)/%, $(my_unstripped_path)) symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem) elf_symbol_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto) ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output) ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_symbol_mapping_path) $(eval $(call copy-unstripped-elf-file-with-mapping,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output),$(elf_symbol_mapping_path))) $(LOCAL_BUILT_MODULE): | $(symbolic_output) ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) my_breakpad_path := $(TARGET_OUT_BREAKPAD)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_symbol_path)) breakpad_output := $(my_breakpad_path)/$(my_installed_module_stem).sym $(breakpad_output) : $(LOCAL_SOONG_UNSTRIPPED_BINARY) | $(BREAKPAD_DUMP_SYMS) $(PRIVATE_READELF) @echo "target breakpad: $(PRIVATE_MODULE) ($@)" @mkdir -p $(dir $@) $(hide) if $(PRIVATE_READELF) -S $< > /dev/null 2>&1 ; then \ $(BREAKPAD_DUMP_SYMS) -c $< > $@ ; \ else \ echo "skipped for non-elf file."; \ touch $@; \ fi $(call add-dependency,$(LOCAL_BUILT_MODULE),$(breakpad_output)) endif endif endif endif ifeq ($(NATIVE_COVERAGE),true) ifneq (,$(strip $(LOCAL_PREBUILT_COVERAGE_ARCHIVE))) $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(intermediates)/$(LOCAL_MODULE).zip)) ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true) ifdef LOCAL_IS_HOST_MODULE my_coverage_path := $($(my_prefix)OUT_COVERAGE)/$(patsubst $($(my_prefix)OUT)/%,%,$(my_module_path)) else my_coverage_path := $(TARGET_OUT_COVERAGE)/$(patsubst $(PRODUCT_OUT)/%,%,$(my_module_path)) endif my_coverage_path := $(my_coverage_path)/$(patsubst %.so,%,$(my_installed_module_stem)).zip $(eval $(call copy-one-file,$(LOCAL_PREBUILT_COVERAGE_ARCHIVE),$(my_coverage_path))) $(LOCAL_BUILT_MODULE): $(my_coverage_path) endif else # Coverage information is needed when static lib is a dependency of another # coverage-enabled module. ifeq (STATIC_LIBRARIES, $(LOCAL_MODULE_CLASS)) GCNO_ARCHIVE := $(LOCAL_MODULE).zip $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(intermediates)/$(GCNO_ARCHIVE) : $(package-coverage-files) endif endif endif # A product may be configured to strip everything in some build variants. # We do the stripping as a post-install command so that LOCAL_BUILT_MODULE # is still with the symbols and we don't need to clean it (and relink) when # you switch build variant. ifneq ($(filter $(STRIP_EVERYTHING_BUILD_VARIANTS),$(TARGET_BUILD_VARIANT)),) $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := \ $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_STRIP) --strip-all $(LOCAL_INSTALLED_MODULE) endif $(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) # Reinstall shared library dependencies of fuzz targets to /data/fuzz/ (for # target) or /data/ (for host). ifdef LOCAL_IS_FUZZ_TARGET $(LOCAL_INSTALLED_MODULE): $(LOCAL_FUZZ_INSTALLED_SHARED_DEPS) endif ================================================ FILE: core/soong_config.mk ================================================ SOONG_MAKEVARS_MK := $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk SOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT)$(COVERAGE_SUFFIX).mk include $(BUILD_SYSTEM)/art_config.mk include $(BUILD_SYSTEM)/dex_preopt_config.mk ifndef AFDO_PROFILES # Set AFDO_PROFILES -include vendor/google_data/pgo_profile/sampling/afdo_profiles.mk include toolchain/pgo-profiles/sampling/afdo_profiles.mk else $(error AFDO_PROFILES can only be set from soong_config.mk. For product-specific fdo_profiles, please use PRODUCT_AFDO_PROFILES) endif # PRODUCT_AFDO_PROFILES takes precedence over product-agnostic profiles in AFDO_PROFILES ALL_AFDO_PROFILES := $(PRODUCT_AFDO_PROFILES) $(AFDO_PROFILES) ifneq (,$(filter-out environment undefined,$(origin GENRULE_SANDBOXING))) $(error GENRULE_SANDBOXING can only be provided via an environment variable, use BUILD_BROKEN_GENRULE_SANDBOXING to disable genrule sandboxing in board config) endif ifeq ($(WRITE_SOONG_VARIABLES),true) # Create soong.variables with copies of makefile settings. Runs every build, # but only updates soong.variables if it changes $(shell mkdir -p $(dir $(SOONG_VARIABLES))) $(call json_start) $(call add_json_str, Make_suffix, -$(TARGET_PRODUCT)$(COVERAGE_SUFFIX)) $(call add_json_str, BuildId, $(BUILD_ID)) $(call add_json_str, BuildFingerprintFile, build_fingerprint.txt) $(call add_json_str, BuildNumberFile, build_number.txt) $(call add_json_str, BuildHostnameFile, build_hostname.txt) $(call add_json_str, BuildThumbprintFile, build_thumbprint.txt) $(call add_json_bool, DisplayBuildNumber, $(filter true,$(DISPLAY_BUILD_NUMBER))) $(call add_json_str, Platform_display_version_name, $(PLATFORM_DISPLAY_VERSION)) $(call add_json_str, Platform_version_name, $(PLATFORM_VERSION)) $(call add_json_val, Platform_sdk_version, $(PLATFORM_SDK_VERSION)) $(call add_json_str, Platform_sdk_codename, $(PLATFORM_VERSION_CODENAME)) $(call add_json_bool, Platform_sdk_final, $(filter REL,$(PLATFORM_VERSION_CODENAME))) $(call add_json_val, Platform_sdk_extension_version, $(PLATFORM_SDK_EXTENSION_VERSION)) $(call add_json_val, Platform_base_sdk_extension_version, $(PLATFORM_BASE_SDK_EXTENSION_VERSION)) $(call add_json_csv, Platform_version_active_codenames, $(PLATFORM_VERSION_ALL_CODENAMES)) $(call add_json_csv, Platform_version_all_preview_codenames, $(PLATFORM_VERSION_ALL_PREVIEW_CODENAMES)) $(call add_json_str, Platform_security_patch, $(PLATFORM_SECURITY_PATCH)) $(call add_json_str, Platform_preview_sdk_version, $(PLATFORM_PREVIEW_SDK_VERSION)) $(call add_json_str, Platform_base_os, $(PLATFORM_BASE_OS)) $(call add_json_str, Platform_version_last_stable, $(PLATFORM_VERSION_LAST_STABLE)) $(call add_json_str, Platform_version_known_codenames, $(PLATFORM_VERSION_KNOWN_CODENAMES)) $(call add_json_bool, Release_aidl_use_unfrozen, $(RELEASE_AIDL_USE_UNFROZEN)) $(call add_json_bool, Allow_missing_dependencies, $(filter true,$(ALLOW_MISSING_DEPENDENCIES))) $(call add_json_bool, Unbundled_build, $(TARGET_BUILD_UNBUNDLED)) $(call add_json_list, Unbundled_build_apps, $(TARGET_BUILD_APPS)) $(call add_json_bool, Unbundled_build_image, $(TARGET_BUILD_UNBUNDLED_IMAGE)) $(call add_json_bool, Always_use_prebuilt_sdks, $(TARGET_BUILD_USE_PREBUILT_SDKS)) $(call add_json_bool, Debuggable, $(filter userdebug eng,$(TARGET_BUILD_VARIANT))) $(call add_json_bool, Eng, $(filter eng,$(TARGET_BUILD_VARIANT))) $(call add_json_str, BuildType, $(TARGET_BUILD_TYPE)) $(call add_json_str, DeviceName, $(TARGET_DEVICE)) $(call add_json_str, DeviceProduct, $(TARGET_PRODUCT)) $(call add_json_str, DeviceArch, $(TARGET_ARCH)) $(call add_json_str, DeviceArchVariant, $(TARGET_ARCH_VARIANT)) $(call add_json_str, DeviceCpuVariant, $(TARGET_CPU_VARIANT)) $(call add_json_list, DeviceAbi, $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2)) $(call add_json_str, DeviceSecondaryArch, $(TARGET_2ND_ARCH)) $(call add_json_str, DeviceSecondaryArchVariant, $(TARGET_2ND_ARCH_VARIANT)) $(call add_json_str, DeviceSecondaryCpuVariant, $(TARGET_2ND_CPU_VARIANT)) $(call add_json_list, DeviceSecondaryAbi, $(TARGET_2ND_CPU_ABI) $(TARGET_2ND_CPU_ABI2)) $(call add_json_bool, Aml_abis, $(if $(filter mainline_sdk,$(TARGET_ARCH_SUITE)),true)) $(call add_json_bool, Ndk_abis, $(if $(filter ndk, $(TARGET_ARCH_SUITE)),true)) $(call add_json_str, NativeBridgeArch, $(TARGET_NATIVE_BRIDGE_ARCH)) $(call add_json_str, NativeBridgeArchVariant, $(TARGET_NATIVE_BRIDGE_ARCH_VARIANT)) $(call add_json_str, NativeBridgeCpuVariant, $(TARGET_NATIVE_BRIDGE_CPU_VARIANT)) $(call add_json_list, NativeBridgeAbi, $(TARGET_NATIVE_BRIDGE_ABI)) $(call add_json_str, NativeBridgeRelativePath, $(TARGET_NATIVE_BRIDGE_RELATIVE_PATH)) $(call add_json_str, NativeBridgeSecondaryArch, $(TARGET_NATIVE_BRIDGE_2ND_ARCH)) $(call add_json_str, NativeBridgeSecondaryArchVariant, $(TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT)) $(call add_json_str, NativeBridgeSecondaryCpuVariant, $(TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT)) $(call add_json_list, NativeBridgeSecondaryAbi, $(TARGET_NATIVE_BRIDGE_2ND_ABI)) $(call add_json_str, NativeBridgeSecondaryRelativePath, $(TARGET_NATIVE_BRIDGE_2ND_RELATIVE_PATH)) $(call add_json_str, HostArch, $(HOST_ARCH)) $(call add_json_str, HostSecondaryArch, $(HOST_2ND_ARCH)) $(call add_json_bool, HostStaticBinaries, $(BUILD_HOST_static)) $(call add_json_bool, HostMusl, $(USE_HOST_MUSL)) $(call add_json_str, CrossHost, $(HOST_CROSS_OS)) $(call add_json_str, CrossHostArch, $(HOST_CROSS_ARCH)) $(call add_json_str, CrossHostSecondaryArch, $(HOST_CROSS_2ND_ARCH)) $(call add_json_list, DeviceResourceOverlays, $(DEVICE_PACKAGE_OVERLAYS)) $(call add_json_list, ProductResourceOverlays, $(PRODUCT_PACKAGE_OVERLAYS)) $(call add_json_list, EnforceRROTargets, $(PRODUCT_ENFORCE_RRO_TARGETS)) $(call add_json_list, EnforceRROExcludedOverlays, $(PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS)) $(call add_json_str, AAPTCharacteristics, $(TARGET_AAPT_CHARACTERISTICS)) $(call add_json_list, AAPTConfig, $(PRODUCT_AAPT_CONFIG)) $(call add_json_str, AAPTPreferredConfig, $(PRODUCT_AAPT_PREF_CONFIG)) $(call add_json_list, AAPTPrebuiltDPI, $(PRODUCT_AAPT_PREBUILT_DPI)) $(call add_json_str, DefaultAppCertificate, $(PRODUCT_DEFAULT_DEV_CERTIFICATE)) $(call add_json_list, ExtraOtaKeys, $(PRODUCT_EXTRA_OTA_KEYS)) $(call add_json_list, ExtraOtaRecoveryKeys, $(PRODUCT_EXTRA_RECOVERY_KEYS)) $(call add_json_str, MainlineSepolicyDevCertificates, $(MAINLINE_SEPOLICY_DEV_CERTIFICATES)) $(call add_json_str, AppsDefaultVersionName, $(APPS_DEFAULT_VERSION_NAME)) $(call add_json_list, SanitizeHost, $(SANITIZE_HOST)) $(call add_json_list, SanitizeDevice, $(SANITIZE_TARGET)) $(call add_json_list, SanitizeDeviceDiag, $(SANITIZE_TARGET_DIAG)) $(call add_json_list, SanitizeDeviceArch, $(SANITIZE_TARGET_ARCH)) $(call add_json_bool, Safestack, $(filter true,$(USE_SAFESTACK))) $(call add_json_bool, EnableCFI, $(call invert_bool,$(filter false,$(ENABLE_CFI)))) $(call add_json_list, CFIExcludePaths, $(CFI_EXCLUDE_PATHS) $(PRODUCT_CFI_EXCLUDE_PATHS)) $(call add_json_list, CFIIncludePaths, $(CFI_INCLUDE_PATHS) $(PRODUCT_CFI_INCLUDE_PATHS)) $(call add_json_list, IntegerOverflowExcludePaths, $(INTEGER_OVERFLOW_EXCLUDE_PATHS) $(PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS)) $(call add_json_list, HWASanIncludePaths, $(HWASAN_INCLUDE_PATHS) $(PRODUCT_HWASAN_INCLUDE_PATHS)) $(call add_json_list, HWASanExcludePaths, $(HWASAN_EXCLUDE_PATHS) $(PRODUCT_HWASAN_EXCLUDE_PATHS)) $(call add_json_list, MemtagHeapExcludePaths, $(MEMTAG_HEAP_EXCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS)) $(call add_json_list, MemtagHeapAsyncIncludePaths, $(MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS) $(if $(filter true,$(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS)),,$(PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS))) $(call add_json_list, MemtagHeapSyncIncludePaths, $(MEMTAG_HEAP_SYNC_INCLUDE_PATHS) $(PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS) $(if $(filter true,$(PRODUCT_MEMTAG_HEAP_SKIP_DEFAULT_PATHS)),,$(PRODUCT_MEMTAG_HEAP_SYNC_DEFAULT_INCLUDE_PATHS))) $(call add_json_bool, DisableScudo, $(filter true,$(PRODUCT_DISABLE_SCUDO))) $(call add_json_bool, ClangTidy, $(filter 1 true,$(WITH_TIDY))) $(call add_json_str, TidyChecks, $(WITH_TIDY_CHECKS)) $(call add_json_list, JavaCoveragePaths, $(JAVA_COVERAGE_PATHS)) $(call add_json_list, JavaCoverageExcludePaths, $(JAVA_COVERAGE_EXCLUDE_PATHS)) $(call add_json_bool, GcovCoverage, $(filter true,$(NATIVE_COVERAGE))) $(call add_json_bool, ClangCoverage, $(filter true,$(CLANG_COVERAGE))) $(call add_json_bool, ClangCoverageContinuousMode, $(filter true,$(CLANG_COVERAGE_CONTINUOUS_MODE))) $(call add_json_list, NativeCoveragePaths, $(NATIVE_COVERAGE_PATHS)) $(call add_json_list, NativeCoverageExcludePaths, $(NATIVE_COVERAGE_EXCLUDE_PATHS)) $(call add_json_bool, ArtUseReadBarrier, $(call invert_bool,$(filter false,$(PRODUCT_ART_USE_READ_BARRIER)))) $(call add_json_str, BtConfigIncludeDir, $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR)) $(call add_json_list, DeviceKernelHeaders, $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS)) $(call add_json_str, VendorApiLevel, $(BOARD_API_LEVEL)) $(call add_json_str, VendorApiLevelPropOverride, $(BOARD_API_LEVEL_PROP_OVERRIDE)) $(call add_json_list, ExtraVndkVersions, $(PRODUCT_EXTRA_VNDK_VERSIONS)) $(call add_json_list, DeviceSystemSdkVersions, $(BOARD_SYSTEMSDK_VERSIONS)) $(call add_json_list, Platform_systemsdk_versions, $(PLATFORM_SYSTEMSDK_VERSIONS)) $(call add_json_bool, Malloc_low_memory, $(findstring true,$(MALLOC_SVELTE) $(MALLOC_LOW_MEMORY))) $(call add_json_bool, Malloc_zero_contents, $(call invert_bool,$(filter false,$(MALLOC_ZERO_CONTENTS)))) $(call add_json_bool, Malloc_pattern_fill_contents, $(MALLOC_PATTERN_FILL_CONTENTS)) $(call add_json_str, Override_rs_driver, $(OVERRIDE_RS_DRIVER)) $(call add_json_str, DeviceMaxPageSizeSupported, $(TARGET_MAX_PAGE_SIZE_SUPPORTED)) $(call add_json_bool, DeviceNoBionicPageSizeMacro, $(filter true,$(TARGET_NO_BIONIC_PAGE_SIZE_MACRO))) $(call add_json_bool, UncompressPrivAppDex, $(call invert_bool,$(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)))) $(call add_json_list, ModulesLoadedByPrivilegedModules, $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES)) $(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS)) $(call add_json_list, ApexBootJars, $(filter-out $(APEX_BOOT_JARS_EXCLUDED), $(PRODUCT_APEX_BOOT_JARS))) $(call add_json_map, BuildFlags) $(foreach flag,$(_ALL_RELEASE_FLAGS),\ $(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).VALUE))) $(call end_json_map) $(call add_json_map, BuildFlagTypes) $(foreach flag,$(_ALL_RELEASE_FLAGS),\ $(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).TYPE))) $(call end_json_map) $(call add_json_bool, MultitreeUpdateMeta, $(filter true,$(TARGET_MULTITREE_UPDATE_META))) $(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES))) $(call add_json_bool, Enforce_vintf_manifest, $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST))) $(call add_json_bool, Uml, $(filter true,$(TARGET_USER_MODE_LINUX))) $(call add_json_str, VendorPath, $(TARGET_COPY_OUT_VENDOR)) $(call add_json_str, VendorDlkmPath, $(TARGET_COPY_OUT_VENDOR_DLKM)) $(call add_json_bool, BuildingVendorImage, $(BUILDING_VENDOR_IMAGE)) $(call add_json_str, OdmPath, $(TARGET_COPY_OUT_ODM)) $(call add_json_bool, BuildingOdmImage, $(BUILDING_ODM_IMAGE)) $(call add_json_str, OdmDlkmPath, $(TARGET_COPY_OUT_ODM_DLKM)) $(call add_json_str, ProductPath, $(TARGET_COPY_OUT_PRODUCT)) $(call add_json_bool, BuildingProductImage, $(BUILDING_PRODUCT_IMAGE)) $(call add_json_str, SystemExtPath, $(TARGET_COPY_OUT_SYSTEM_EXT)) $(call add_json_str, SystemDlkmPath, $(TARGET_COPY_OUT_SYSTEM_DLKM)) $(call add_json_str, OemPath, $(TARGET_COPY_OUT_OEM)) $(call add_json_bool, MinimizeJavaDebugInfo, $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO))) $(call add_json_str, RecoveryPath, $(TARGET_COPY_OUT_RECOVERY)) $(call add_json_bool, BuildingRecoveryImage, $(BUILDING_RECOVERY_IMAGE)) $(call add_json_str, UserdataPath, $(TARGET_COPY_OUT_DATA)) $(call add_json_bool, BuildingUserdataImage, $(BUILDING_USERDATA_IMAGE)) $(call add_json_bool, UseGoma, $(filter-out false,$(USE_GOMA))) $(call add_json_bool, UseRBE, $(filter-out false,$(USE_RBE))) $(call add_json_bool, UseRBEJAVAC, $(filter-out false,$(RBE_JAVAC))) $(call add_json_bool, UseRBER8, $(filter-out false,$(RBE_R8))) $(call add_json_bool, UseRBED8, $(filter-out false,$(RBE_D8))) $(call add_json_bool, Arc, $(filter true,$(TARGET_ARC))) $(call add_json_list, NamespacesToExport, $(PRODUCT_SOONG_NAMESPACES)) $(call add_json_list, PgoAdditionalProfileDirs, $(PGO_ADDITIONAL_PROFILE_DIRS)) $(call add_json_list, BoardVendorSepolicyDirs, $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS)) $(call add_json_list, BoardOdmSepolicyDirs, $(BOARD_ODM_SEPOLICY_DIRS)) $(call add_json_list, SystemExtPublicSepolicyDirs, $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS)) $(call add_json_list, SystemExtPrivateSepolicyDirs, $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS)) $(call add_json_list, BoardSepolicyM4Defs, $(BOARD_SEPOLICY_M4DEFS)) $(call add_json_str, BoardSepolicyVers, $(BOARD_SEPOLICY_VERS)) $(call add_json_str, SystemExtSepolicyPrebuiltApiDir, $(BOARD_SYSTEM_EXT_PREBUILT_DIR)) $(call add_json_str, ProductSepolicyPrebuiltApiDir, $(BOARD_PRODUCT_PREBUILT_DIR)) $(call add_json_str, BoardPlatform, $(TARGET_BOARD_PLATFORM)) $(call add_json_str, PlatformSepolicyVersion, $(PLATFORM_SEPOLICY_VERSION)) $(call add_json_list, PlatformSepolicyCompatVersions, $(PLATFORM_SEPOLICY_COMPAT_VERSIONS)) $(call add_json_bool, ForceApexSymlinkOptimization, $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION))) $(call add_json_str, DexpreoptGlobalConfig, $(DEX_PREOPT_CONFIG)) $(call add_json_bool, WithDexpreopt, $(filter true,$(WITH_DEXPREOPT))) $(call add_json_list, ManifestPackageNameOverrides, $(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES)) $(call add_json_list, PackageNameOverrides, $(PRODUCT_PACKAGE_NAME_OVERRIDES)) $(call add_json_list, CertificateOverrides, $(PRODUCT_CERTIFICATE_OVERRIDES)) $(call add_json_list, ConfiguredJarLocationOverrides, $(PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES)) $(call add_json_str, ApexGlobalMinSdkVersionOverride, $(APEX_GLOBAL_MIN_SDK_VERSION_OVERRIDE)) $(call add_json_bool, EnforceSystemCertificate, $(filter true,$(ENFORCE_SYSTEM_CERTIFICATE))) $(call add_json_list, EnforceSystemCertificateAllowList, $(ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST)) $(call add_json_list, ProductHiddenAPIStubs, $(PRODUCT_HIDDENAPI_STUBS)) $(call add_json_list, ProductHiddenAPIStubsSystem, $(PRODUCT_HIDDENAPI_STUBS_SYSTEM)) $(call add_json_list, ProductHiddenAPIStubsTest, $(PRODUCT_HIDDENAPI_STUBS_TEST)) $(call add_json_list, ProductPublicSepolicyDirs, $(PRODUCT_PUBLIC_SEPOLICY_DIRS)) $(call add_json_list, ProductPrivateSepolicyDirs, $(PRODUCT_PRIVATE_SEPOLICY_DIRS)) $(call add_json_list, TargetFSConfigGen, $(TARGET_FS_CONFIG_GEN)) # Although USE_SOONG_DEFINED_SYSTEM_IMAGE determines whether to use the system image specified by # PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE, PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE is still used to compare # installed files between make and soong, regardless of the USE_SOONG_DEFINED_SYSTEM_IMAGE setting. $(call add_json_bool, UseSoongSystemImage, $(filter true,$(USE_SOONG_DEFINED_SYSTEM_IMAGE))) $(call add_json_str, ProductSoongDefinedSystemImage, $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE)) $(call add_json_bool, UseSoongNoticeXML, $(filter true,$(PRODUCT_USE_SOONG_NOTICE_XML))) $(call add_json_map, VendorVars) $(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\ $(call add_json_map, $(namespace))\ $(foreach key,$(sort $(SOONG_CONFIG_$(namespace))),\ $(call add_json_str,$(key),$(subst ",\",$(SOONG_CONFIG_$(namespace)_$(key)))))\ $(call end_json_map)) $(call end_json_map) # Add the types of the variables in VendorVars. Since this is much newer # than VendorVars, which has a history of just using string values for everything, # variables are assumed to be strings by default. For strings, SOONG_CONFIG_TYPE_* # will not be set, and they will not have an entry in the VendorVarTypes map. $(call add_json_map, VendorVarTypes) $(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\ $(call add_json_map, $(namespace))\ $(foreach key,$(sort $(SOONG_CONFIG_$(namespace))),\ $(if $(SOONG_CONFIG_TYPE_$(namespace)_$(key)),$(call add_json_str,$(key),$(subst ",\",$(SOONG_CONFIG_TYPE_$(namespace)_$(key))))))\ $(call end_json_map)) $(call end_json_map) $(call add_json_bool, EnforceProductPartitionInterface, $(filter true,$(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE))) $(call add_json_str, DeviceCurrentApiLevelForVendorModules, $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES)) $(call add_json_bool, CompressedApex, $(filter true,$(PRODUCT_COMPRESSED_APEX))) $(call add_json_str, DefaultApexPayloadType, $(PRODUCT_DEFAULT_APEX_PAYLOAD_TYPE)) $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) $(call add_json_list, BoardKernelBinaries, $(BOARD_KERNEL_BINARIES)) $(call add_json_list, BoardKernelModuleInterfaceVersions, $(BOARD_KERNEL_MODULE_INTERFACE_VERSIONS)) $(call add_json_bool, BoardMoveRecoveryResourcesToVendorBoot, $(filter true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))) $(call add_json_str, PrebuiltHiddenApiDir, $(BOARD_PREBUILT_HIDDENAPI_DIR)) $(call add_json_str, Shipping_api_level, $(PRODUCT_SHIPPING_API_LEVEL)) $(call add_json_list, BuildBrokenPluginValidation, $(BUILD_BROKEN_PLUGIN_VALIDATION)) $(call add_json_bool, BuildBrokenClangProperty, $(filter true,$(BUILD_BROKEN_CLANG_PROPERTY))) $(call add_json_bool, BuildBrokenClangAsFlags, $(filter true,$(BUILD_BROKEN_CLANG_ASFLAGS))) $(call add_json_bool, BuildBrokenClangCFlags, $(filter true,$(BUILD_BROKEN_CLANG_CFLAGS))) # Use the value of GENRULE_SANDBOXING if set, otherwise use the inverse of BUILD_BROKEN_GENRULE_SANDBOXING $(call add_json_bool, GenruleSandboxing, $(if $(GENRULE_SANDBOXING),$(filter true,$(GENRULE_SANDBOXING)),$(if $(filter true,$(BUILD_BROKEN_GENRULE_SANDBOXING)),,true))) $(call add_json_bool, BuildBrokenEnforceSyspropOwner, $(filter true,$(BUILD_BROKEN_ENFORCE_SYSPROP_OWNER))) $(call add_json_bool, BuildBrokenTrebleSyspropNeverallow, $(filter true,$(BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW))) $(call add_json_bool, BuildBrokenVendorPropertyNamespace, $(filter true,$(BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE))) $(call add_json_bool, BuildBrokenIncorrectPartitionImages, $(filter true,$(BUILD_BROKEN_INCORRECT_PARTITION_IMAGES))) $(call add_json_list, BuildBrokenInputDirModules, $(BUILD_BROKEN_INPUT_DIR_MODULES)) $(call add_json_bool, BuildBrokenDontCheckSystemSdk, $(filter true,$(BUILD_BROKEN_DONT_CHECK_SYSTEMSDK))) $(call add_json_bool, BuildBrokenDupSysprop, $(filter true,$(BUILD_BROKEN_DUP_SYSPROP))) $(call add_json_list, BuildWarningBadOptionalUsesLibsAllowlist, $(BUILD_WARNING_BAD_OPTIONAL_USES_LIBS_ALLOWLIST)) $(call add_json_bool, BuildDebugfsRestrictionsEnabled, $(filter true,$(PRODUCT_SET_DEBUGFS_RESTRICTIONS))) $(call add_json_bool, RequiresInsecureExecmemForSwiftshader, $(filter true,$(PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER))) $(call add_json_bool, SelinuxIgnoreNeverallows, $(filter true,$(SELINUX_IGNORE_NEVERALLOWS))) $(call add_json_list, SepolicyFreezeTestExtraDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_DIRS)) $(call add_json_list, SepolicyFreezeTestExtraPrebuiltDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS)) $(call add_json_bool, GenerateAidlNdkPlatformBackend, $(filter true,$(NEED_AIDL_NDK_PLATFORM_BACKEND))) $(call add_json_bool, IgnorePrefer32OnDevice, $(filter true,$(IGNORE_PREFER32_ON_DEVICE))) $(call add_json_list, SourceRootDirs, $(PRODUCT_SOURCE_ROOT_DIRS)) $(call add_json_list, AfdoProfiles, $(ALL_AFDO_PROFILES)) $(call add_json_str, ProductManufacturer, $(PRODUCT_MANUFACTURER)) $(call add_json_str, ProductBrand, $(PRODUCT_BRAND)) $(call add_json_str, ProductDevice, $(PRODUCT_DEVICE)) $(call add_json_str, ProductModel, $(PRODUCT_MODEL)) $(call add_json_str, ReleaseVersion, $(_RELEASE_VERSION)) $(call add_json_list, ReleaseAconfigValueSets, $(RELEASE_ACONFIG_VALUE_SETS)) $(call add_json_str, ReleaseAconfigFlagDefaultPermission, $(RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION)) $(call add_json_bool, ReleaseDefaultModuleBuildFromSource, $(RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE)) $(call add_json_bool, CheckVendorSeappViolations, $(filter true,$(CHECK_VENDOR_SEAPP_VIOLATIONS))) $(call add_json_bool, BuildIgnoreApexContributionContents, $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS)) $(call add_json_bool, BuildFromSourceStub, $(findstring true,$(PRODUCT_BUILD_FROM_SOURCE_STUB) $(BUILD_FROM_SOURCE_STUB))) $(call add_json_bool, HiddenapiExportableStubs, $(filter true,$(PRODUCT_HIDDEN_API_EXPORTABLE_STUBS))) $(call add_json_bool, ExportRuntimeApis, $(filter true,$(PRODUCT_EXPORT_RUNTIME_APIS))) $(call add_json_str, AconfigContainerValidation, $(ACONFIG_CONTAINER_VALIDATION)) $(call add_json_list, ProductLocales, $(subst _,-,$(PRODUCT_LOCALES))) $(call add_json_list, ProductDefaultWifiChannels, $(PRODUCT_DEFAULT_WIFI_CHANNELS)) $(call add_json_bool, BoardUseVbmetaDigestInFingerprint, $(filter true,$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT))) $(call add_json_list, OemProperties, $(PRODUCT_OEM_PROPERTIES)) $(call add_json_list, SystemPropFiles, $(TARGET_SYSTEM_PROP)) $(call add_json_list, SystemExtPropFiles, $(TARGET_SYSTEM_EXT_PROP)) $(call add_json_list, ProductPropFiles, $(TARGET_PRODUCT_PROP)) $(call add_json_list, OdmPropFiles, $(TARGET_ODM_PROP)) $(call add_json_list, VendorPropFiles, $(TARGET_VENDOR_PROP)) # Do not set ArtTargetIncludeDebugBuild into any value if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD is not set, # to have the same behavior from runtime_libart.mk. ifneq ($(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD),) $(call add_json_bool, ArtTargetIncludeDebugBuild, $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)) endif _config_enable_uffd_gc := \ $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default) $(call add_json_str, EnableUffdGc, $(_config_enable_uffd_gc)) _config_enable_uffd_gc := $(call add_json_str, BoardKernelVersion, $(BOARD_KERNEL_VERSION)) $(call add_json_list, DeviceFrameworkCompatibilityMatrixFile, $(DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE)) $(call add_json_list, DeviceProductCompatibilityMatrixFile, $(DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE)) $(call add_json_list, BoardAvbSystemAddHashtreeFooterArgs, $(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS)) $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE))) $(call add_json_str, AdbKeys, $(PRODUCT_ADB_KEYS)) $(call add_json_map, PartitionVarsForSoongMigrationOnlyDoNotUse) $(call add_json_str, ProductDirectory, $(dir $(INTERNAL_PRODUCT))) $(call add_json_map,PartitionQualifiedVariables) $(foreach image_type,INIT_BOOT BOOT VENDOR_BOOT SYSTEM VENDOR CACHE USERDATA PRODUCT SYSTEM_EXT OEM ODM VENDOR_DLKM ODM_DLKM SYSTEM_DLKM VBMETA VBMETA_SYSTEM VBMETA_SYSTEM_DLKM VBMETA_VENDOR_DLKM, \ $(call add_json_map,$(call to-lower,$(image_type))) \ $(call add_json_bool, BuildingImage, $(filter true,$(BUILDING_$(image_type)_IMAGE))) \ $(call add_json_bool, PrebuiltImage, $(filter true,$(BOARD_PREBUILT_$(image_type)IMAGE))) \ $(call add_json_str, BoardErofsCompressor, $(BOARD_$(image_type)IMAGE_EROFS_COMPRESSOR)) \ $(call add_json_str, BoardErofsCompressHints, $(BOARD_$(image_type)IMAGE_EROFS_COMPRESS_HINTS)) \ $(call add_json_str, BoardErofsPclusterSize, $(BOARD_$(image_type)IMAGE_EROFS_PCLUSTER_SIZE)) \ $(call add_json_str, BoardExtfsInodeCount, $(BOARD_$(image_type)IMAGE_EXTFS_INODE_COUNT)) \ $(call add_json_str, BoardExtfsRsvPct, $(BOARD_$(image_type)IMAGE_EXTFS_RSV_PCT)) \ $(call add_json_str, BoardF2fsSloadCompressFlags, $(BOARD_$(image_type)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)) \ $(call add_json_str, BoardFileSystemCompress, $(BOARD_$(image_type)IMAGE_FILE_SYSTEM_COMPRESS)) \ $(call add_json_str, BoardFileSystemType, $(BOARD_$(image_type)IMAGE_FILE_SYSTEM_TYPE)) \ $(call add_json_str, BoardJournalSize, $(BOARD_$(image_type)IMAGE_JOURNAL_SIZE)) \ $(call add_json_str, BoardPartitionReservedSize, $(BOARD_$(image_type)IMAGE_PARTITION_RESERVED_SIZE)) \ $(call add_json_str, BoardPartitionSize, $(BOARD_$(image_type)IMAGE_PARTITION_SIZE)) \ $(call add_json_str, BoardSquashfsBlockSize, $(BOARD_$(image_type)IMAGE_SQUASHFS_BLOCK_SIZE)) \ $(call add_json_str, BoardSquashfsCompressor, $(BOARD_$(image_type)IMAGE_SQUASHFS_COMPRESSOR)) \ $(call add_json_str, BoardSquashfsCompressorOpt, $(BOARD_$(image_type)IMAGE_SQUASHFS_COMPRESSOR_OPT)) \ $(call add_json_str, BoardSquashfsDisable4kAlign, $(BOARD_$(image_type)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)) \ $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_$(image_type)_KEY_PATH)) \ $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_$(image_type)_ALGORITHM)) \ $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX)) \ $(call add_json_str, BoardAvbRollbackIndexLocation, $(BOARD_AVB_$(image_type)_ROLLBACK_INDEX_LOCATION)) \ $(call add_json_str, BoardAvbAddHashtreeFooterArgs, $(BOARD_AVB_$(image_type)_ADD_HASHTREE_FOOTER_ARGS)) \ $(call add_json_str, ProductBaseFsPath, $(PRODUCT_$(image_type)_BASE_FS_PATH)) \ $(call add_json_str, ProductHeadroom, $(PRODUCT_$(image_type)_HEADROOM)) \ $(call add_json_str, ProductVerityPartition, $(PRODUCT_$(image_type)_VERITY_PARTITION)) \ $(call end_json_map) \ ) $(call end_json_map) $(call add_json_bool, TargetUserimagesUseExt2, $(filter true,$(TARGET_USERIMAGES_USE_EXT2))) $(call add_json_bool, TargetUserimagesUseExt3, $(filter true,$(TARGET_USERIMAGES_USE_EXT3))) $(call add_json_bool, TargetUserimagesUseExt4, $(filter true,$(TARGET_USERIMAGES_USE_EXT4))) $(call add_json_bool, TargetUserimagesSparseExtDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED))) $(call add_json_bool, TargetUserimagesSparseErofsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED))) $(call add_json_bool, TargetUserimagesSparseSquashfsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED))) $(call add_json_bool, TargetUserimagesSparseF2fsDisabled, $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED))) $(call add_json_str, BoardErofsCompressor, $(BOARD_EROFS_COMPRESSOR)) $(call add_json_str, BoardErofsCompressorHints, $(BOARD_EROFS_COMPRESS_HINTS)) $(call add_json_str, BoardErofsPclusterSize, $(BOARD_EROFS_PCLUSTER_SIZE)) $(call add_json_str, BoardErofsShareDupBlocks, $(BOARD_EROFS_SHARE_DUP_BLOCKS)) $(call add_json_str, BoardErofsUseLegacyCompression, $(BOARD_EROFS_USE_LEGACY_COMPRESSION)) $(call add_json_str, BoardExt4ShareDupBlocks, $(BOARD_EXT4_SHARE_DUP_BLOCKS)) $(call add_json_str, BoardFlashLogicalBlockSize, $(BOARD_FLASH_LOGICAL_BLOCK_SIZE)) $(call add_json_str, BoardFlashEraseBlockSize, $(BOARD_FLASH_ERASE_BLOCK_SIZE)) $(call add_json_bool, BuildingVbmetaImage, $(BUILDING_VBMETA_IMAGE)) # boot image stuff $(call add_json_bool, BuildingRamdiskImage, $(filter true,$(BUILDING_RAMDISK_IMAGE))) $(call add_json_bool, ProductBuildBootImage, $(filter true,$(PRODUCT_BUILD_BOOT_IMAGE))) $(call add_json_str, ProductBuildVendorBootImage, $(PRODUCT_BUILD_VENDOR_BOOT_IMAGE)) $(call add_json_bool, ProductBuildInitBootImage, $(filter true,$(PRODUCT_BUILD_INIT_BOOT_IMAGE))) $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) $(call add_json_str, BoardPrebuiltBootimage, $(BOARD_PREBUILT_BOOT_IMAGE)) $(call add_json_str, BoardPrebuiltInitBootimage, $(BOARD_PREBUILT_INIT_BOOT_IMAGE)) $(call add_json_str, BoardBootimagePartitionSize, $(BOARD_BOOTIMAGE_PARTITION_SIZE)) $(call add_json_str, BoardVendorBootimagePartitionSize, $(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(call add_json_str, BoardInitBootimagePartitionSize, $(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) $(call add_json_str, BoardBootHeaderVersion, $(BOARD_BOOT_HEADER_VERSION)) $(call add_json_str, TargetKernelPath, $(TARGET_KERNEL_PATH)) $(call add_json_bool, BoardUsesGenericKernelImage, $(BOARD_USES_GENERIC_KERNEL_IMAGE)) $(call add_json_str, BootSecurityPatch, $(BOOT_SECURITY_PATCH)) $(call add_json_str, InitBootSecurityPatch, $(INIT_BOOT_SECURITY_PATCH)) $(call add_json_str, VendorSecurityPatch, $(VENDOR_SECURITY_PATCH)) $(call add_json_str, OdmSecurityPatch, $(ODM_SECURITY_PATCH)) $(call add_json_str, SystemDlkmSecurityPatch, $(SYSTEM_DLKM_SECURITY_PATCH)) $(call add_json_str, VendorDlkmSecurityPatch, $(VENDOR_DLKM_SECURITY_PATCH)) $(call add_json_str, OdmDlkmSecurityPatch, $(ODM_DLKM_SECURITY_PATCH)) $(call add_json_bool, BoardIncludeDtbInBootimg, $(BOARD_INCLUDE_DTB_IN_BOOTIMG)) $(call add_json_list, InternalKernelCmdline, $(INTERNAL_KERNEL_CMDLINE)) $(call add_json_list, InternalBootconfig, $(INTERNAL_BOOTCONFIG)) $(call add_json_str, InternalBootconfigFile, $(INTERNAL_BOOTCONFIG_FILE)) $(call add_json_bool, BuildingSystemOtherImage, $(BUILDING_SYSTEM_OTHER_IMAGE)) # super image stuff $(call add_json_bool, ProductUseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))) $(call add_json_bool, ProductRetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS))) $(call add_json_bool, ProductBuildSuperPartition, $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION))) $(call add_json_bool, BuildingSuperEmptyImage, $(filter true,$(BUILDING_SUPER_EMPTY_IMAGE))) $(call add_json_str, BoardSuperPartitionSize, $(BOARD_SUPER_PARTITION_SIZE)) $(call add_json_str, BoardSuperPartitionMetadataDevice, $(BOARD_SUPER_PARTITION_METADATA_DEVICE)) $(call add_json_list, BoardSuperPartitionBlockDevices, $(BOARD_SUPER_PARTITION_BLOCK_DEVICES)) $(call add_json_map, BoardSuperPartitionGroups) $(foreach group, $(BOARD_SUPER_PARTITION_GROUPS), \ $(call add_json_map, $(group)) \ $(call add_json_str, GroupSize, $(BOARD_$(call to-upper,$(group))_SIZE)) \ $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \ $(call add_json_list, PartitionList, $(BOARD_$(call to-upper,$(group))_PARTITION_LIST))) \ $(call end_json_map)) $(call end_json_map) $(call add_json_bool, ProductVirtualAbOta, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA))) $(call add_json_bool, ProductVirtualAbOtaRetrofit, $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT))) $(call add_json_bool, ProductVirtualAbCompression, $(filter true,$(PRODUCT_VIRTUAL_AB_COMPRESSION))) $(call add_json_str, ProductVirtualAbCompressionMethod, $(PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD)) $(call add_json_str, ProductVirtualAbCompressionFactor, $(PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR)) $(call add_json_str, ProductVirtualAbCowVersion, $(PRODUCT_VIRTUAL_AB_COW_VERSION)) $(call add_json_bool, AbOtaUpdater, $(filter true,$(AB_OTA_UPDATER))) $(call add_json_list, AbOtaPartitions, $(AB_OTA_PARTITIONS)) $(call add_json_list, AbOtaKeys, $(PRODUCT_OTA_PUBLIC_KEYS)) $(call add_json_list, AbOtaPostInstallConfig, $(AB_OTA_POSTINSTALL_CONFIG)) $(call add_json_bool, BoardSuperImageInUpdatePackage, $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE))) # Avb (android verified boot) stuff $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE))) $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_ALGORITHM)) $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_KEY_PATH)) $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_ROLLBACK_INDEX)) $(call add_json_map, ChainedVbmetaPartitions) $(foreach partition,system vendor $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\ $(call add_json_map, $(partition)) \ $(call add_json_list,Partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))) \ $(call add_json_str,Key,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)) \ $(call add_json_str,Algorithm,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)) \ $(call add_json_str,RollbackIndex,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX)) \ $(call add_json_str,RollbackIndexLocation,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)) \ $(call end_json_map)) $(call end_json_map) $(call add_json_bool, ProductUseDynamicPartitionSize, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE))) $(call add_json_bool, CopyImagesForTargetFilesZip, $(filter true,$(COPY_IMAGES_FOR_TARGET_FILES_ZIP))) $(call add_json_list, ProductPackages, $(PRODUCT_PACKAGES)) $(call add_json_list, ProductPackagesDebug, $(PRODUCT_PACKAGES_DEBUG)) # Used to generate /vendor/linker.config.pb $(call add_json_list, VendorLinkerConfigSrcs, $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS)) $(call add_json_list, ProductLinkerConfigSrcs, $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS)) # Used to generate _dlkm partitions $(call add_json_bool, BuildingSystemDlkmImage, $(BUILDING_SYSTEM_DLKM_IMAGE)) $(call add_json_list, SystemKernelModules, $(BOARD_SYSTEM_KERNEL_MODULES)) $(call add_json_str, SystemKernelBlocklistFile, $(BOARD_SYSTEM_KERNEL_MODULES_BLOCKLIST_FILE)) $(call add_json_list, SystemKernelLoadModules, $(BOARD_SYSTEM_KERNEL_MODULES_LOAD)) $(call add_json_bool, BuildingVendorDlkmImage, $(BUILDING_VENDOR_DLKM_IMAGE)) $(call add_json_list, VendorKernelModules, $(BOARD_VENDOR_KERNEL_MODULES)) $(call add_json_str, VendorKernelBlocklistFile, $(BOARD_VENDOR_KERNEL_MODULES_BLOCKLIST_FILE)) $(call add_json_bool, BuildingOdmDlkmImage, $(BUILDING_ODM_DLKM_IMAGE)) $(call add_json_list, OdmKernelModules, $(BOARD_ODM_KERNEL_MODULES)) $(call add_json_str, OdmKernelBlocklistFile, $(BOARD_ODM_KERNEL_MODULES_BLOCKLIST_FILE)) $(call add_json_list, VendorRamdiskKernelModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES)) $(call add_json_str, VendorRamdiskKernelBlocklistFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_BLOCKLIST_FILE)) $(call add_json_list, VendorRamdiskKernelLoadModules, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD)) $(call add_json_str, VendorRamdiskKernelOptionsFile, $(BOARD_VENDOR_RAMDISK_KERNEL_MODULES_OPTIONS_FILE)) # Used to generate /vendor/build.prop $(call add_json_list, BoardInfoFiles, $(if $(TARGET_BOARD_INFO_FILES),$(TARGET_BOARD_INFO_FILES),$(firstword $(TARGET_BOARD_INFO_FILE) $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt)))) $(call add_json_str, BootLoaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME)) $(call add_json_list, ProductCopyFiles, $(PRODUCT_COPY_FILES)) # Used to generate fsv meta $(call add_json_bool, ProductFsverityGenerateMetadata, $(PRODUCT_FSVERITY_GENERATE_METADATA)) # Used to generate recovery partition $(call add_json_str, TargetScreenDensity, $(TARGET_SCREEN_DENSITY)) # Used to generate /recovery/root/build.prop $(call add_json_map, PrivateRecoveryUiProperties) $(call add_json_str, animation_fps, $(TARGET_RECOVERY_UI_ANIMATION_FPS)) $(call add_json_str, margin_height, $(TARGET_RECOVERY_UI_MARGIN_HEIGHT)) $(call add_json_str, margin_width, $(TARGET_RECOVERY_UI_MARGIN_WIDTH)) $(call add_json_str, menu_unusable_rows, $(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS)) $(call add_json_str, progress_bar_baseline, $(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE)) $(call add_json_str, touch_low_threshold, $(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD)) $(call add_json_str, touch_high_threshold, $(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD)) $(call add_json_str, vr_stereo_offset, $(TARGET_RECOVERY_UI_VR_STEREO_OFFSET)) $(call add_json_str, brightness_file, $(TARGET_RECOVERY_UI_BRIGHTNESS_FILE)) $(call add_json_str, max_brightness_file, $(TARGET_RECOVERY_UI_MAX_BRIGHTNESS_FILE)) $(call add_json_str, brightness_normal_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_NORMAL)) $(call add_json_str, brightness_dimmed_percent, $(TARGET_RECOVERY_UI_BRIGHTNESS_DIMMED)) $(call end_json_map) $(call add_json_str, PrebuiltBootloader, $(BOARD_PREBUILT_BOOTLOADER)) # Used to generate userdata partition $(call add_json_str, ProductFsCasefold, $(PRODUCT_FS_CASEFOLD)) $(call add_json_str, ProductQuotaProjid, $(PRODUCT_QUOTA_PROJID)) $(call add_json_str, ProductFsCompression, $(PRODUCT_FS_COMPRESSION)) $(call add_json_str, ReleaseToolsExtensionDir, $(firstword $(TARGET_RELEASETOOLS_EXTENSIONS) $($(TARGET_DEVICE_DIR)/../common))) $(call add_json_list, BoardPartialOtaUpdatePartitionsList, $(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)) $(call add_json_str, BoardFlashBlockSize, $(BOARD_FLASH_BLOCK_SIZE)) $(call add_json_bool, BootloaderInUpdatePackage, $(BOARD_BOOTLOADER_IN_UPDATE_PACKAGE)) # Fastboot $(call add_json_str, BoardFastbootInfoFile, $(TARGET_BOARD_FASTBOOT_INFO_FILE)) $(call end_json_map) # For converting vintf_data $(call add_json_list, DeviceMatrixFile, $(DEVICE_MATRIX_FILE)) $(call add_json_list, ProductManifestFiles, $(PRODUCT_MANIFEST_FILES)) $(call add_json_list, SystemManifestFile, $(DEVICE_FRAMEWORK_MANIFEST_FILE)) SYSTEM_EXT_HWSERVICE_FILES := ifeq ($(PRODUCT_HIDL_ENABLED),true) ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),) SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager_no_max.xml else $(error If PRODUCT_HIDL_ENABLED is set, hwservicemanager must be added to PRODUCT_PACKAGES explicitly) endif else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),) SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)),) SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml endif endif $(call add_json_list, SystemExtManifestFiles, $(SYSTEM_EXT_MANIFEST_FILES) $(SYSTEM_EXT_HWSERVICE_FILES)) $(call add_json_list, DeviceManifestFiles, $(DEVICE_MANIFEST_FILE)) $(call add_json_list, OdmManifestFiles, $(ODM_MANIFEST_FILES)) $(call json_end) $(file >$(SOONG_VARIABLES).tmp,$(json_contents)) $(shell if ! cmp -s $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); then \ mv $(SOONG_VARIABLES).tmp $(SOONG_VARIABLES); \ else \ rm $(SOONG_VARIABLES).tmp; \ fi) include $(BUILD_SYSTEM)/soong_extra_config.mk endif # CONFIGURE_SOONG ================================================ FILE: core/soong_droiddoc_prebuilt.mk ================================================ # Droiddoc prebuilt coming from Soong. ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,soong_droiddoc_prebuilt.mk may only be used from Soong) endif ifdef LOCAL_DROIDDOC_STUBS_SRCJAR $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_STUBS_SRCJAR),$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar)) $(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar .PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE) : $(OUT_DOCS)/$(LOCAL_MODULE)-stubs.srcjar endif ifdef LOCAL_DROIDDOC_DOC_ZIP $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_DOC_ZIP),$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip)) $(eval ALL_TARGETS.$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) $(call dist-for-goals,docs,$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip) .PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip $(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip : $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip endif ifdef LOCAL_DROIDDOC_ANNOTATIONS_ZIP $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_ANNOTATIONS_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip)) $(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) endif ifdef LOCAL_DROIDDOC_API_VERSIONS_XML $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_API_VERSIONS_XML),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml)) $(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_generated-api-versions.xml.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) endif ifdef LOCAL_DROIDDOC_METADATA_ZIP $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_METADATA_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip)) $(eval ALL_TARGETS.$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)-metadata.zip.META_LIC := $(LOCAL_SOONG_LICENSE_METADATA)) endif ================================================ FILE: core/soong_extra_config.mk ================================================ $(call json_start) $(call add_json_str, DeviceCpuVariantRuntime, $(TARGET_CPU_VARIANT_RUNTIME)) $(call add_json_str, DeviceAbiList, $(TARGET_CPU_ABI_LIST)) $(call add_json_str, DeviceAbiList32, $(TARGET_CPU_ABI_LIST_32_BIT)) $(call add_json_str, DeviceAbiList64, $(TARGET_CPU_ABI_LIST_64_BIT)) $(call add_json_str, DeviceSecondaryCpuVariantRuntime, $(TARGET_2ND_CPU_VARIANT_RUNTIME)) $(call add_json_str, Dex2oatTargetCpuVariantRuntime, $(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME)) $(call add_json_str, Dex2oatTargetInstructionSetFeatures, $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) $(call add_json_str, SecondaryDex2oatCpuVariantRuntime, $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME)) $(call add_json_str, SecondaryDex2oatInstructionSetFeatures, $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)) $(call add_json_str, BoardPlatform, $(TARGET_BOARD_PLATFORM)) $(call add_json_str, BoardShippingApiLevel, $(BOARD_SHIPPING_API_LEVEL)) $(call add_json_str, ShippingApiLevel, $(PRODUCT_SHIPPING_API_LEVEL)) $(call add_json_str, ProductModel, $(PRODUCT_MODEL)) $(call add_json_str, ProductModelForAttestation, $(PRODUCT_MODEL_FOR_ATTESTATION)) $(call add_json_str, ProductBrandForAttestation, $(PRODUCT_BRAND_FOR_ATTESTATION)) $(call add_json_str, ProductNameForAttestation, $(PRODUCT_NAME_FOR_ATTESTATION)) $(call add_json_str, ProductDeviceForAttestation, $(PRODUCT_DEVICE_FOR_ATTESTATION)) $(call add_json_str, ProductManufacturerForAttestation, $(PRODUCT_MANUFACTURER_FOR_ATTESTATION)) $(call add_json_str, SystemBrand, $(PRODUCT_SYSTEM_BRAND)) $(call add_json_str, SystemDevice, $(PRODUCT_SYSTEM_DEVICE)) $(call add_json_str, SystemManufacturer, $(PRODUCT_SYSTEM_MANUFACTURER)) $(call add_json_str, SystemModel, $(PRODUCT_SYSTEM_MODEL)) $(call add_json_str, SystemName, $(PRODUCT_SYSTEM_NAME)) # Collapses ?= and = operators for system property variables. Also removes double quotes to prevent # malformed JSON. This change aligns with the existing behavior of sysprop.mk, which passes property # variables to the echo command, effectively discarding surrounding double quotes. define collapse-prop-pairs $(subst ",,$(call collapse-pairs,$(call collapse-pairs,$$($(1)),?=),=)) endef $(call add_json_list, PRODUCT_SYSTEM_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_SYSTEM_PROPERTIES)) $(call add_json_list, PRODUCT_SYSTEM_DEFAULT_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_SYSTEM_DEFAULT_PROPERTIES)) $(call add_json_list, PRODUCT_SYSTEM_EXT_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_SYSTEM_EXT_PROPERTIES)) $(call add_json_list, PRODUCT_VENDOR_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_VENDOR_PROPERTIES)) $(call add_json_list, PRODUCT_PRODUCT_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_PRODUCT_PROPERTIES)) $(call add_json_list, PRODUCT_ODM_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_ODM_PROPERTIES)) $(call add_json_list, PRODUCT_PROPERTY_OVERRIDES, $(call collapse-prop-pairs,PRODUCT_PROPERTY_OVERRIDES)) $(call add_json_list, PRODUCT_DEFAULT_PROPERTY_OVERRIDES, $(call collapse-prop-pairs,PRODUCT_DEFAULT_PROPERTY_OVERRIDES)) $(call add_json_str, BootloaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME)) $(call add_json_bool, SdkBuild, $(filter sdk sdk_addon,$(MAKECMDGOALS))) $(call add_json_str, SystemServerCompilerFilter, $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)) $(call add_json_bool, Product16KDeveloperOption, $(filter true,$(PRODUCT_16K_DEVELOPER_OPTION))) $(call add_json_str, RecoveryDefaultRotation, $(TARGET_RECOVERY_DEFAULT_ROTATION)) $(call add_json_str, RecoveryOverscanPercent, $(TARGET_RECOVERY_OVERSCAN_PERCENT)) $(call add_json_str, RecoveryPixelFormat, $(TARGET_RECOVERY_PIXEL_FORMAT)) ifdef AB_OTA_UPDATER $(call add_json_bool, AbOtaUpdater, $(filter true,$(AB_OTA_UPDATER))) $(call add_json_str, AbOtaPartitions, $(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS)))) endif ifdef PRODUCT_USE_DYNAMIC_PARTITIONS $(call add_json_bool, UseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))) endif ifdef PRODUCT_RETROFIT_DYNAMIC_PARTITIONS $(call add_json_bool, RetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS))) endif $(call add_json_bool, DontUseVabcOta, $(filter true,$(BOARD_DONT_USE_VABC_OTA))) $(call add_json_bool, FullTreble, $(filter true,$(PRODUCT_FULL_TREBLE))) $(call add_json_bool, NoBionicPageSizeMacro, $(filter true,$(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO))) $(call add_json_bool, PropertySplitEnabled, $(filter true,$(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED))) $(call add_json_str, ScreenDensity, $(TARGET_SCREEN_DENSITY)) $(call add_json_str, UsesVulkan, $(TARGET_USES_VULKAN)) $(call add_json_bool, ZygoteForce64, $(filter true,$(ZYGOTE_FORCE_64))) $(call add_json_str, VendorSecurityPatch, $(VENDOR_SECURITY_PATCH)) $(call add_json_str, VendorImageFileSystemType, $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)) $(call add_json_list, BuildVersionTags, $(BUILD_VERSION_TAGS)) $(call add_json_bool, ProductNotDebuggableInUserdebug, $(PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG)) $(call add_json_bool, UsesProductImage, $(filter true,$(BOARD_USES_PRODUCTIMAGE))) $(call add_json_bool, TargetBoots16K, $(filter true,$(TARGET_BOOTS_16K))) $(call json_end) $(shell mkdir -p $(dir $(SOONG_EXTRA_VARIABLES))) $(file >$(SOONG_EXTRA_VARIABLES).tmp,$(json_contents)) $(shell if ! cmp -s $(SOONG_EXTRA_VARIABLES).tmp $(SOONG_EXTRA_VARIABLES); then \ mv $(SOONG_EXTRA_VARIABLES).tmp $(SOONG_EXTRA_VARIABLES); \ else \ rm $(SOONG_EXTRA_VARIABLES).tmp; \ fi) ================================================ FILE: core/soong_java_prebuilt.mk ================================================ # Java prebuilt coming from Soong. # Extra inputs: # LOCAL_SOONG_BUILT_INSTALLED # LOCAL_SOONG_CLASSES_JAR # LOCAL_SOONG_HEADER_JAR # LOCAL_SOONG_DEX_JAR # LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR # LOCAL_SOONG_DEXPREOPT_CONFIG ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) $(call pretty-error,soong_java_prebuilt.mk may only be used from Soong) endif LOCAL_MODULE_SUFFIX := .jar LOCAL_BUILT_MODULE_STEM := javalib.jar intermediates.COMMON := $(call local-intermediates-dir,COMMON) full_classes_jar := $(intermediates.COMMON)/classes.jar full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar common_javalib.jar := $(intermediates.COMMON)/javalib.jar ####################################### include $(BUILD_SYSTEM)/base_rules.mk ####################################### ifdef LOCAL_SOONG_CLASSES_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar))) $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar))) $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar))) ifneq ($(TURBINE_ENABLED),false) ifdef LOCAL_SOONG_HEADER_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar))) else $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar))) endif endif # TURBINE_ENABLED != false endif $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE))) ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar)) $(call add-dependency,$(common_javalib.jar),\ $(call local-packaging-dir,jacoco)/jacoco-report-classes.jar) endif ifdef LOCAL_SOONG_PROGUARD_DICT $(eval $(call copy-r8-dictionary-file-with-mapping,\ $(LOCAL_SOONG_PROGUARD_DICT),\ $(intermediates.COMMON)/proguard_dictionary,\ $(intermediates.COMMON)/proguard_dictionary.textproto)) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \ $(intermediates.COMMON)/proguard_dictionary \ $(LOCAL_SOONG_CLASSES_JAR) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \ -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \ -f $(intermediates.COMMON)/proguard_dictionary \ -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \ -f $(LOCAL_SOONG_CLASSES_JAR) ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto endif ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP) endif ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE my_res_package := $(intermediates.COMMON)/package-res.apk $(my_res_package): $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE) @echo "Copy: $@" $(copy-file-to-target) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_res_package)) my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages $(eval $(call copy-one-file,$(LOCAL_SOONG_TRANSITIVE_RES_PACKAGES),$(my_transitive_res_packages))) $(call add-dependency,$(my_res_package),$(my_transitive_res_packages)) my_proguard_flags := $(intermediates.COMMON)/export_proguard_flags $(eval $(call copy-one-file,$(LOCAL_SOONG_EXPORT_PROGUARD_FLAGS),$(my_proguard_flags))) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_proguard_flags)) my_static_library_extra_packages := $(intermediates.COMMON)/extra_packages $(eval $(call copy-one-file,$(LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES),$(my_static_library_extra_packages))) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_extra_packages)) my_static_library_android_manifest := $(intermediates.COMMON)/manifest/AndroidManifest.xml $(eval $(call copy-one-file,$(LOCAL_FULL_MANIFEST_FILE),$(my_static_library_android_manifest))) $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_android_manifest)) endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE ifdef LOCAL_SOONG_DEX_JAR ifndef LOCAL_IS_HOST_MODULE boot_jars := $(foreach pair,$(PRODUCT_BOOT_JARS), $(call word-colon,2,$(pair))) ifneq ($(filter $(LOCAL_MODULE),$(boot_jars)),) # is_boot_jar ifeq (true,$(WITH_DEXPREOPT)) # dex_bootjars singleton installs all of bootjars' dexpreopt files (.art, .oat, .vdex, ...) # This includes both the primary and secondary arches. # Add them to the required list so they are installed alongside this module. ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += dex_bootjars # Copy $(LOCAL_BUILT_MODULE) and its dependencies when installing boot.art # so that dependencies of $(LOCAL_BUILT_MODULE) (which may include # jacoco-report-classes.jar) are copied for every build. $(foreach m,dex_bootjars, \ $(eval $(call add-dependency,$(firstword $(call module-installed-files,$(m))),$(LOCAL_BUILT_MODULE))) \ ) endif endif # is_boot_jar $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar))) $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(common_javalib.jar))) ifdef LOCAL_SOONG_CLASSES_JAR $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar))) ifneq ($(TURBINE_ENABLED),false) $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_header_jar))) endif endif endif java-dex : $(LOCAL_BUILT_MODULE) else # LOCAL_SOONG_DEX_JAR ifndef LOCAL_UNINSTALLABLE_MODULE ifndef LOCAL_IS_HOST_MODULE $(call pretty-error,Installable device module must have LOCAL_SOONG_DEX_JAR set) endif endif endif # LOCAL_SOONG_DEX_JAR ALL_MODULES.$(my_register_name).CLASSES_JAR := $(full_classes_jar) ifdef LOCAL_SOONG_AAR ALL_MODULES.$(my_register_name).AAR := $(LOCAL_SOONG_AAR) endif # Copy dexpreopt.config files from Soong libraries to the location where Make # modules can find them. ifdef LOCAL_SOONG_DEXPREOPT_CONFIG $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(call local-intermediates-dir,)/dexpreopt.config)) my_dexpreopt_config := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config $(eval $(call copy-one-file,$(LOCAL_SOONG_DEXPREOPT_CONFIG), $(my_dexpreopt_config))) $(LOCAL_BUILT_MODULE): $(my_dexpreopt_config) endif ifdef LOCAL_SOONG_CLASSES_JAR javac-check : $(full_classes_jar) javac-check-$(LOCAL_MODULE) : $(full_classes_jar) endif .PHONY: javac-check-$(LOCAL_MODULE) ifndef LOCAL_IS_HOST_MODULE ifeq ($(LOCAL_SDK_VERSION),system_current) my_link_type := java:system else ifneq (,$(call has-system-sdk-version,$(LOCAL_SDK_VERSION))) my_link_type := java:system else ifeq ($(LOCAL_SDK_VERSION),core_current) my_link_type := java:core else ifneq ($(LOCAL_SDK_VERSION),) my_link_type := java:sdk else my_link_type := java:platform endif # warn/allowed types are both empty because Soong modules can't depend on # make-defined modules. my_warn_types := my_allowed_types := my_link_deps := my_2nd_arch_prefix := $(LOCAL_2ND_ARCH_VAR_PREFIX) my_common := COMMON include $(BUILD_SYSTEM)/link_type.mk endif # !LOCAL_IS_HOST_MODULE # LOCAL_EXPORT_SDK_LIBRARIES set by soong is written to exported-sdk-libs file my_exported_sdk_libs_file := $(intermediates.COMMON)/exported-sdk-libs $(my_exported_sdk_libs_file): PRIVATE_EXPORTED_SDK_LIBS := $(LOCAL_EXPORT_SDK_LIBRARIES) $(my_exported_sdk_libs_file): @echo "Export SDK libs $@" $(hide) mkdir -p $(dir $@) && rm -f $@ $(if $(PRIVATE_EXPORTED_SDK_LIBS),\ $(hide) echo $(PRIVATE_EXPORTED_SDK_LIBS) | tr ' ' '\n' > $@,\ $(hide) touch $@) SOONG_ALREADY_CONV += $(LOCAL_MODULE) ================================================ FILE: core/static_java_library.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Standard rules for building a "static" java library. # Static java libraries are not installed, nor listed on any # classpaths. They can, however, be included wholesale in # other java modules. $(call record-module-type,STATIC_JAVA_LIBRARY) LOCAL_UNINSTALLABLE_MODULE := true LOCAL_IS_STATIC_JAVA_LIBRARY := true LOCAL_MODULE_CLASS := JAVA_LIBRARIES intermediates.COMMON := $(call local-intermediates-dir,COMMON) my_res_package := # Process Support Library dependencies. include $(BUILD_SYSTEM)/support_libraries.mk include $(BUILD_SYSTEM)/force_aapt2.mk # Hack to build static Java library with Android resource # See bug 5714516 all_resources := need_compile_res := # A static Java library needs to explicily set LOCAL_RESOURCE_DIR. ifdef LOCAL_RESOURCE_DIR need_compile_res := true LOCAL_RESOURCE_DIR := $(foreach d,$(LOCAL_RESOURCE_DIR),$(call clean-path,$(d))) endif ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) need_compile_res := true endif ifeq ($(need_compile_res),true) all_resources := $(strip \ $(foreach dir, $(LOCAL_RESOURCE_DIR), \ $(addprefix $(dir)/, \ $(patsubst res/%,%, \ $(call find-subdir-assets,$(dir)) \ ) \ ) \ )) # By default we should remove the R/Manifest classes from a static Java library, # because they will be regenerated in the app that uses it. # But if the static Java library will be used by a library, then we may need to # keep the generated classes with "LOCAL_JAR_EXCLUDE_FILES := none". ifndef LOCAL_JAR_EXCLUDE_FILES LOCAL_JAR_EXCLUDE_FILES := $(ANDROID_RESOURCE_GENERATED_CLASSES) endif ifeq (none,$(LOCAL_JAR_EXCLUDE_FILES)) LOCAL_JAR_EXCLUDE_FILES := endif proguard_options_file := ifneq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom) proguard_options_file := $(intermediates.COMMON)/proguard_options endif LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS) LOCAL_PROGUARD_FLAGS_DEPS += $(proguard_options_file) R_file_stamp := $(intermediates.COMMON)/src/R.stamp LOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp) ifneq ($(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),) # If we are using static android libraries, every source file becomes an overlay. # This is to emulate old AAPT behavior which simulated library support. my_res_resources := my_overlay_resources := $(all_resources) else # Otherwise, for a library we treat all the resource equal with no overlay. my_res_resources := $(all_resources) my_overlay_resources := endif # For libraries put everything in the COMMON intermediate directory. my_res_package := $(intermediates.COMMON)/package-res.apk LOCAL_INTERMEDIATE_TARGETS += $(my_res_package) endif # need_compile_res all_res_assets := $(all_resources) include $(BUILD_SYSTEM)/java_renderscript.mk ifeq (true,$(need_compile_res)) # work around missing manifests by creating a default one ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE))) ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml)) LOCAL_FULL_MANIFEST_FILE := $(call local-intermediates-dir,COMMON)/DefaultManifest.xml $(call create-default-manifest-file,$(LOCAL_FULL_MANIFEST_FILE),$(call module-min-sdk-version)) endif endif include $(BUILD_SYSTEM)/android_manifest.mk LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION)) ifeq ($(LOCAL_SDK_RES_VERSION),) LOCAL_SDK_RES_VERSION:=$(LOCAL_SDK_VERSION) endif framework_res_package_export := # Please refer to package.mk ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true) ifneq ($(filter-out current system_current test_current,$(LOCAL_SDK_RES_VERSION))$(if $(TARGET_BUILD_USE_PREBUILT_SDKS),$(filter current system_current test_current,$(LOCAL_SDK_RES_VERSION))),) framework_res_package_export := \ $(call resolve-prebuilt-sdk-jar-path,$(LOCAL_SDK_RES_VERSION)) else framework_res_package_export := \ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk endif endif # transitive-res-packages is only populated for Soong modules for now, but needs # to exist so that other Make modules can depend on it. Create an empty file. my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages $(my_transitive_res_packages): touch $@ import_proguard_flag_files := $(strip $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\ $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags)) $(intermediates.COMMON)/export_proguard_flags: $(import_proguard_flag_files) $(addprefix $(LOCAL_PATH)/,$(LOCAL_EXPORT_PROGUARD_FLAG_FILES)) @echo "Export proguard flags: $@" rm -f $@ touch $@ for f in $+; do \ echo -e "\n# including $$f" >>$@; \ cat $$f >>$@; \ done import_proguard_flag_files := include $(BUILD_SYSTEM)/aapt_flags.mk $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(TARGET_AAPT_CHARACTERISTICS) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_MANIFEST_PACKAGE_NAME) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := $(LOCAL_MANIFEST_INSTRUMENTATION_FOR) # add --non-constant-id to prevent inlining constants. # AAR needs text symbol file R.txt. $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) --static-lib --output-text-symbols $(intermediates.COMMON)/R.txt ifndef LOCAL_AAPT_NAMESPACES $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_FLAGS += --no-static-lib-packages endif $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_CONFIG := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PRODUCT_AAPT_PREF_CONFIG := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_AAPT_CHARACTERISTICS := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_PUBLICS_OUTPUT := $(intermediates.COMMON)/public_resources.xml $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_INCLUDES := $(framework_res_package_export) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_PACKAGE_NAME := $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_MANIFEST_INSTRUMENTATION_FOR := # One more level with name res so we can zip up the flat resources that can be linked by apps. my_compiled_res_base_dir := $(intermediates.COMMON)/flat-res/res ifneq (,$(filter-out current,$(renderscript_target_api))) ifneq ($(call math_gt_or_eq,$(renderscript_target_api),21),true) my_generated_res_zips := $(rs_generated_res_zip) endif # renderscript_target_api < 21 endif # renderscript_target_api is set include $(BUILD_SYSTEM)/aapt2.mk $(my_res_package) : $(framework_res_package_export) $(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(intermediates.COMMON)/R.txt endif # need_compile_res include $(BUILD_SYSTEM)/java_library.mk ifeq (true,$(need_compile_res)) $(LOCAL_BUILT_MODULE): $(R_file_stamp) $(java_source_list_file): $(R_file_stamp) $(full_classes_compiled_jar): $(R_file_stamp) $(full_classes_turbine_jar): $(R_file_stamp) # if we have custom proguarding done use the proguarded classes jar instead of the normal classes jar ifeq ($(filter custom,$(LOCAL_PROGUARD_ENABLED)),custom) aar_classes_jar = $(full_classes_jar) else aar_classes_jar = $(full_classes_pre_proguard_jar) endif # Rule to build AAR, archive including classes.jar, resource, etc. built_aar := $(intermediates.COMMON)/javalib.aar $(built_aar): PRIVATE_MODULE := $(LOCAL_MODULE) $(built_aar): PRIVATE_ANDROID_MANIFEST := $(full_android_manifest) $(built_aar): PRIVATE_CLASSES_JAR := $(aar_classes_jar) $(built_aar): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) $(built_aar): PRIVATE_R_TXT := $(intermediates.COMMON)/R.txt $(built_aar): $(JAR_ARGS) $(built_aar) : $(aar_classes_jar) $(full_android_manifest) $(intermediates.COMMON)/R.txt @echo "target AAR: $(PRIVATE_MODULE) ($@)" $(hide) rm -rf $(dir $@)aar && mkdir -p $(dir $@)aar/res $(hide) cp $(PRIVATE_ANDROID_MANIFEST) $(dir $@)aar/AndroidManifest.xml $(hide) cp $(PRIVATE_CLASSES_JAR) $(dir $@)aar/classes.jar # Note: Use "cp -n" to honor the resource overlay rules, if multiple res dirs exist. $(hide) $(foreach res,$(PRIVATE_RESOURCE_DIR),cp -Rfn $(res)/* $(dir $@)aar/res;) $(hide) cp $(PRIVATE_R_TXT) $(dir $@)aar/R.txt $(hide) $(JAR) -cMf $@ \ $(call jar-args-sorted-files-in-directory,$(dir $@)aar) # Register the aar file. ALL_MODULES.$(my_register_name).AAR := $(built_aar) endif # need_compile_res # Reset internal variables. aar_classes_jar := all_res_assets := LOCAL_IS_STATIC_JAVA_LIBRARY := $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=STATIC_JAVA_LIBRARY)) ================================================ FILE: core/static_library.mk ================================================ $(call record-module-type,STATIC_LIBRARY) ifdef LOCAL_IS_HOST_MODULE $(call pretty-error,BUILD_STATIC_LIBRARY is incompatible with LOCAL_IS_HOST_MODULE. Use BUILD_HOST_STATIC_LIBRARY instead) endif my_prefix := TARGET_ include $(BUILD_SYSTEM)/multilib.mk ifndef my_module_multilib # libraries default to building for both architecturess my_module_multilib := both endif LOCAL_2ND_ARCH_VAR_PREFIX := include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) include $(BUILD_SYSTEM)/static_library_internal.mk endif ifdef TARGET_2ND_ARCH LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX) include $(BUILD_SYSTEM)/module_arch_supported.mk ifeq ($(my_module_arch_supported),true) # Build for TARGET_2ND_ARCH LOCAL_BUILT_MODULE := LOCAL_INSTALLED_MODULE := LOCAL_INTERMEDIATE_TARGETS := include $(BUILD_SYSTEM)/static_library_internal.mk endif LOCAL_2ND_ARCH_VAR_PREFIX := endif # TARGET_2ND_ARCH my_module_arch_supported := ########################################################### ## Copy headers to the install tree ########################################################### ifdef LOCAL_COPY_HEADERS $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\ $(call pretty-warning,LOCAL_COPY_HEADERS is deprecated. See $(CHANGES_URL)#copy_headers),\ $(call pretty-error,LOCAL_COPY_HEADERS is obsolete. See $(CHANGES_URL)#copy_headers)) include $(BUILD_SYSTEM)/copy_headers.mk endif ================================================ FILE: core/static_library_internal.mk ================================================ ########################################################### ## Standard rules for building a static library. ## ## Additional inputs from base_rules.make: ## None. ## ## LOCAL_MODULE_SUFFIX will be set for you. ########################################################### ifeq ($(strip $(LOCAL_MODULE_CLASS)),) LOCAL_MODULE_CLASS := STATIC_LIBRARIES endif ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),) LOCAL_MODULE_SUFFIX := .a endif LOCAL_UNINSTALLABLE_MODULE := true ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),) $(error $(LOCAL_PATH): Cannot set module stem for a library) endif include $(BUILD_SYSTEM)/binary.mk $(LOCAL_BUILT_MODULE) : $(built_whole_libraries) $(LOCAL_BUILT_MODULE) : $(all_objects) $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_AR) $(transform-o-to-static-lib) ifeq ($(NATIVE_COVERAGE),true) gcno_suffix := .zip built_whole_gcno_libraries := \ $(foreach lib,$(my_whole_static_libraries), \ $(call intermediates-dir-for, \ STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX), \ $(my_host_cross))/$(lib)$(gcno_suffix)) GCNO_ARCHIVE := $(LOCAL_MODULE)$(gcno_suffix) $(intermediates)/$(GCNO_ARCHIVE) : $(SOONG_ZIP) $(MERGE_ZIPS) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_OBJECTS := $(strip $(LOCAL_GCNO_FILES)) $(intermediates)/$(GCNO_ARCHIVE) : PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(strip $(built_whole_gcno_libraries)) $(intermediates)/$(GCNO_ARCHIVE) : $(LOCAL_GCNO_FILES) $(built_whole_gcno_libraries) $(package-coverage-files) endif $(if $(my_register_name),$(eval ALL_MODULES.$(my_register_name).MAKE_MODULE_TYPE:=STATIC_LIBRARY)) ================================================ FILE: core/suite_host_config.mk ================================================ # # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # LOCAL_MODULE_CLASS := FAKE LOCAL_IS_HOST_MODULE := true include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): @echo "$(LOCAL_COMPATIBILITY_SUITE) host-driven test target: $(PRIVATE_MODULE)" $(hide) touch $@ ================================================ FILE: core/support_libraries.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ########################################################### ## Rules for resolving Support Library dependencies. ## ## The following variables may be modified: ## - LOCAL_JAVA_LIBRARIES ## - LOCAL_STATIC_JAVA_LIBRARIES ## - LOCAL_SHARED_ANDROID_LIBRARIES ## - LOCAL_STATIC_ANDROID_LIBRARIES ########################################################### # Some projects don't work correctly yet. Allow them to skip resolution. ifndef LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES # Aggregate all requested Support Library modules. requested_support_libs := $(filter $(SUPPORT_LIBRARIES_JARS) $(SUPPORT_LIBRARIES_AARS), \ $(LOCAL_JAVA_LIBRARIES) $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(LOCAL_SHARED_ANDROID_LIBRARIES) $(LOCAL_STATIC_ANDROID_LIBRARIES)) # Filter the Support Library modules out of the library variables. We don't # trust developers to get these right, so they will be added back by the # build system based on the output of this file and the type of build. LOCAL_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \ $(LOCAL_JAVA_LIBRARIES)) LOCAL_STATIC_JAVA_LIBRARIES := $(filter-out $(requested_support_libs), \ $(LOCAL_STATIC_JAVA_LIBRARIES)) LOCAL_SHARED_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \ $(LOCAL_SHARED_ANDROID_LIBRARIES)) LOCAL_STATIC_ANDROID_LIBRARIES := $(filter-out $(requested_support_libs), \ $(LOCAL_STATIC_ANDROID_LIBRARIES)) LOCAL_STATIC_ANDROID_LIBRARIES := $(strip $(LOCAL_STATIC_ANDROID_LIBRARIES) \ $(filter $(SUPPORT_LIBRARIES_AARS),$(requested_support_libs))) LOCAL_STATIC_JAVA_LIBRARIES := $(strip $(LOCAL_STATIC_JAVA_LIBRARIES) \ $(filter $(SUPPORT_LIBRARIES_JARS),$(requested_support_libs))) endif #LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES := ================================================ FILE: core/sysprop.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # sysprop.mk defines rules for generating /[etc/]build.prop files # ----------------------------------------------------------------- # property_overrides_split_enabled property_overrides_split_enabled := ifeq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true) property_overrides_split_enabled := true endif POST_PROCESS_PROPS := $(HOST_OUT_EXECUTABLES)/post_process_props$(HOST_EXECUTABLE_SUFFIX) # Emits a set of sysprops common to all partitions to a file. # $(1): Partition name # $(2): Output file name define generate-common-build-props echo "####################################" >> $(2);\ echo "# from generate-common-build-props" >> $(2);\ echo "# These properties identify this partition image." >> $(2);\ echo "####################################" >> $(2);\ echo "ro.product.$(1).brand=$(PRODUCT_BRAND)" >> $(2);\ echo "ro.product.$(1).device=$(TARGET_DEVICE)" >> $(2);\ echo "ro.product.$(1).manufacturer=$(PRODUCT_MANUFACTURER)" >> $(2);\ echo "ro.product.$(1).model=$(PRODUCT_MODEL)" >> $(2);\ echo "ro.product.$(1).name=$(TARGET_PRODUCT)" >> $(2);\ if [ -n "$(strip $(PRODUCT_MODEL_FOR_ATTESTATION))" ]; then \ echo "ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)" >> $(2);\ fi; \ if [ -n "$(strip $(PRODUCT_BRAND_FOR_ATTESTATION))" ]; then \ echo "ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)" >> $(2);\ fi; \ if [ -n "$(strip $(PRODUCT_NAME_FOR_ATTESTATION))" ]; then \ echo "ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)" >> $(2);\ fi; \ if [ -n "$(strip $(PRODUCT_DEVICE_FOR_ATTESTATION))" ]; then \ echo "ro.product.device_for_attestation=$(PRODUCT_DEVICE_FOR_ATTESTATION)" >> $(2);\ fi; \ if [ -n "$(strip $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))" ]; then \ echo "ro.product.manufacturer_for_attestation=$(PRODUCT_MANUFACTURER_FOR_ATTESTATION)" >> $(2);\ fi; \ $(if $(filter true,$(ZYGOTE_FORCE_64)),\ $(if $(filter vendor,$(1)),\ echo "ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ echo "ro.$(1).product.cpu.abilist32=" >> $(2);\ echo "ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ )\ ,\ $(if $(filter system vendor odm,$(1)),\ echo "ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST)" >> $(2);\ echo "ro.$(1).product.cpu.abilist32=$(TARGET_CPU_ABI_LIST_32_BIT)" >> $(2);\ echo "ro.$(1).product.cpu.abilist64=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\ )\ )\ echo "ro.$(1).build.date=`$(DATE_FROM_FILE)`" >> $(2);\ echo "ro.$(1).build.date.utc=`$(DATE_FROM_FILE) +%s`" >> $(2);\ # Allow optional assignments for ARC forward-declarations (b/249168657) # TODO: Remove any tag-related inconsistencies once the goals from # go/arc-android-sigprop-changes have been achieved. echo "ro.$(1).build.fingerprint?=$(BUILD_FINGERPRINT_FROM_FILE)" >> $(2);\ echo "ro.$(1).build.id?=$(BUILD_ID)" >> $(2);\ echo "ro.$(1).build.tags?=$(BUILD_VERSION_TAGS)" >> $(2);\ echo "ro.$(1).build.type=$(TARGET_BUILD_VARIANT)" >> $(2);\ echo "ro.$(1).build.version.incremental=$(BUILD_NUMBER_FROM_FILE)" >> $(2);\ echo "ro.$(1).build.version.release=$(PLATFORM_VERSION_LAST_STABLE)" >> $(2);\ echo "ro.$(1).build.version.release_or_codename=$(PLATFORM_VERSION)" >> $(2);\ echo "ro.$(1).build.version.sdk=$(PLATFORM_SDK_VERSION)" >> $(2);\ echo "ro.$(1).build.version.sdk_minor=$(PLATFORM_SDK_MINOR_VERSION)" >> $(2);\ endef # Rule for generating /[etc/]build.prop file # # $(1): partition name # $(2): path to the output # $(3): path to the input *.prop files. The contents of the files are directly # emitted to the output # $(4): list of variable names each of which contains name=value pairs # $(5): optional list of prop names to force remove from the output. Properties from both # $(3) and (4) are affected # $(6): optional list of files to append at the end. The content of each file is emitted # to the output # $(7): optional flag to skip common properties generation define build-properties ALL_DEFAULT_INSTALLED_MODULES += $(2) $(eval # Properties can be assigned using `prop ?= value` or `prop = value` syntax.) $(eval # Eliminate spaces around the ?= and = separators.) $(foreach name,$(strip $(4)),\ $(eval _temp := $$(call collapse-pairs,$$($(name)),?=))\ $(eval _resolved_$(name) := $$(call collapse-pairs,$$(_temp),=))\ ) $(eval # Implement the legacy behavior when BUILD_BROKEN_DUP_SYSPROP is on.) $(eval # Optional assignments are all converted to normal assignments and) $(eval # when their duplicates the first one wins) $(if $(filter true,$(BUILD_BROKEN_DUP_SYSPROP)),\ $(foreach name,$(strip $(4)),\ $(eval _temp := $$(subst ?=,=,$$(_resolved_$(name))))\ $(eval _resolved_$(name) := $$(call uniq-pairs-by-first-component,$$(_resolved_$(name)),=))\ )\ $(eval _option := --allow-dup)\ ) $(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6) $(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC) $(hide) echo Building $$@ $(hide) mkdir -p $$(dir $$@) $(hide) rm -f $$@ && touch $$@ ifneq ($(strip $(7)), true) $(hide) $$(call generate-common-build-props,$(call to-lower,$(strip $(1))),$$@) endif # Make and Soong use different intermediate files to build vendor/build.prop. # Although the sysprop contents are same, the absolute paths of android-info.prop are different. # Print the filename for the intermediate files (files in OUT_DIR). # This helps with validating mk->soong migration of android partitions. $(hide) $(foreach file,$(strip $(3)),\ if [ -f "$(file)" ]; then\ echo "" >> $$@;\ echo "####################################" >> $$@;\ $(if $(filter $(OUT_DIR)/%,$(file)), \ echo "# from $(notdir $(file))" >> $$@;\ ,\ echo "# from $(file)" >> $$@;\ )\ echo "####################################" >> $$@;\ cat $(file) >> $$@;\ fi;) $(hide) $(foreach name,$(strip $(4)),\ echo "" >> $$@;\ echo "####################################" >> $$@;\ echo "# from variable $(name)" >> $$@;\ echo "####################################" >> $$@;\ $$(foreach line,$$(_resolved_$(name)),\ echo "$$(line)" >> $$@;\ )\ ) $(hide) $(POST_PROCESS_PROPS) $$(_option) \ --sdk-version $(PLATFORM_SDK_VERSION) \ --kernel-version-file-for-uffd-gc "$(BUILT_KERNEL_VERSION_FILE_FOR_UFFD_GC)" \ $$@ $(5) $(hide) $(foreach file,$(strip $(6)),\ if [ -f "$(file)" ]; then\ cat $(file) >> $$@;\ fi;) $(hide) echo "# end of file" >> $$@ $(call declare-1p-target,$(2)) endef KNOWN_OEM_THUMBPRINT_PROPERTIES := \ ro.product.brand \ ro.product.name \ ro.product.device OEM_THUMBPRINT_PROPERTIES := $(filter $(KNOWN_OEM_THUMBPRINT_PROPERTIES),\ $(PRODUCT_OEM_PROPERTIES)) KNOWN_OEM_THUMBPRINT_PROPERTIES:= # ----------------------------------------------------------------- # system/build.prop # # system/build.prop is built by Soong. See system-build.prop module in # build/soong/Android.bp. INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop # ----------------------------------------------------------------- # vendor/build.prop # _prop_files_ := $(if $(TARGET_VENDOR_PROP),\ $(TARGET_VENDOR_PROP),\ $(wildcard $(TARGET_DEVICE_DIR)/vendor.prop)) android_info_prop := $(call intermediates-dir-for,ETC,android_info_prop)/android-info.prop $(android_info_prop): $(INSTALLED_ANDROID_INFO_TXT_TARGET) cat $< | grep 'require version-' | sed -e 's/require version-/ro.build.expect./g' > $@ _prop_files_ += $(android_info_prop) ifdef property_overrides_split_enabled # Order matters here. When there are duplicates, the last one wins. # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter _prop_vars_ := \ ADDITIONAL_VENDOR_PROPERTIES \ PRODUCT_VENDOR_PROPERTIES # TODO(b/117892318): deprecate this _prop_vars_ += \ PRODUCT_DEFAULT_PROPERTY_OVERRIDES \ PRODUCT_PROPERTY_OVERRIDES else _prop_vars_ := endif INSTALLED_VENDOR_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR)/build.prop $(eval $(call build-properties,\ vendor,\ $(INSTALLED_VENDOR_BUILD_PROP_TARGET),\ $(_prop_files_),\ $(_prop_vars_),\ $(PRODUCT_VENDOR_PROPERTY_BLACKLIST),\ $(empty),\ $(empty))) $(eval $(call declare-1p-target,$(INSTALLED_VENDOR_BUILD_PROP_TARGET))) # ----------------------------------------------------------------- # product/etc/build.prop # # product/etc/build.prop is built by Soong. See product-build.prop module in # build/soong/Android.bp. INSTALLED_PRODUCT_BUILD_PROP_TARGET := $(TARGET_OUT_PRODUCT)/etc/build.prop # ---------------------------------------------------------------- # odm/etc/build.prop # # odm/etc/build.prop is built by Soong. See odm-build.prop module in # build/soong/Android.bp. INSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/etc/build.prop # ---------------------------------------------------------------- # vendor_dlkm/etc/build.prop # odm_dlkm/etc/build.prop # system_dlkm/build.prop # These are built by Soong. See build/soong/Android.bp INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop INSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_DLKM)/etc/build.prop ALL_DEFAULT_INSTALLED_MODULES += \ $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \ $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \ $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \ # ----------------------------------------------------------------- # system_ext/etc/build.prop # # system_ext/etc/build.prop is built by Soong. See system-build.prop module in # build/soong/Android.bp. INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_EXT)/etc/build.prop RAMDISK_BUILD_PROP_REL_PATH := system/etc/ramdisk/build.prop ifeq (true,$(BOARD_USES_RECOVERY_AS_BOOT)) INSTALLED_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/$(RAMDISK_BUILD_PROP_REL_PATH) else INSTALLED_RAMDISK_BUILD_PROP_TARGET := $(TARGET_RAMDISK_OUT)/$(RAMDISK_BUILD_PROP_REL_PATH) endif ALL_INSTALLED_BUILD_PROP_FILES := \ $(INSTALLED_BUILD_PROP_TARGET) \ $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \ $(INSTALLED_PRODUCT_BUILD_PROP_TARGET) \ $(INSTALLED_ODM_BUILD_PROP_TARGET) \ $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \ $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \ $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \ $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET) \ $(INSTALLED_RAMDISK_BUILD_PROP_TARGET) # $1 installed file path, e.g. out/target/product/vsoc_x86_64/system/build.prop define is-build-prop $(if $(findstring $1,$(ALL_INSTALLED_BUILD_PROP_FILES)),Y) endef ================================================ FILE: core/sysprop_config.mk ================================================ # ADDITIONAL__PROPERTIES are properties that are determined by the # build system itself. Don't let it be defined from outside of the core build # system like Android.mk or .mk files. _additional_prop_var_names := \ ADDITIONAL_SYSTEM_PROPERTIES \ ADDITIONAL_VENDOR_PROPERTIES \ ADDITIONAL_ODM_PROPERTIES \ ADDITIONAL_PRODUCT_PROPERTIES $(foreach name, $(_additional_prop_var_names),\ $(if $($(name)),\ $(error $(name) must not set before here. $($(name)))\ ,)\ $(eval $(name) :=)\ ) _additional_prop_var_names := $(KATI_obsolete_var ADDITIONAL_SYSTEM_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead) $(KATI_obsolete_var ADDITIONAL_ODM_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead) $(KATI_obsolete_var ADDITIONAL_PRODUCT_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead) # Add cpu properties for bionic and ART. ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH) ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.cpu_variant=$(TARGET_CPU_VARIANT_RUNTIME) ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_arch=$(TARGET_2ND_ARCH) ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_cpu_variant=$(TARGET_2ND_CPU_VARIANT_RUNTIME) ADDITIONAL_VENDOR_PROPERTIES += persist.sys.dalvik.vm.lib.2=libart.so ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).variant=$(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME) ifneq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) endif ifdef TARGET_2ND_ARCH ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).variant=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_CPU_VARIANT_RUNTIME) ifneq ($($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_2ND_ARCH).features=$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) endif endif # Although these variables are prefixed with TARGET_RECOVERY_, they are also needed under charger # mode (via libminui). ifdef TARGET_RECOVERY_DEFAULT_ROTATION ADDITIONAL_VENDOR_PROPERTIES += \ ro.minui.default_rotation=$(TARGET_RECOVERY_DEFAULT_ROTATION) endif ifdef TARGET_RECOVERY_OVERSCAN_PERCENT ADDITIONAL_VENDOR_PROPERTIES += \ ro.minui.overscan_percent=$(TARGET_RECOVERY_OVERSCAN_PERCENT) endif ifdef TARGET_RECOVERY_PIXEL_FORMAT ADDITIONAL_VENDOR_PROPERTIES += \ ro.minui.pixel_format=$(TARGET_RECOVERY_PIXEL_FORMAT) endif ifdef PRODUCT_USE_DYNAMIC_PARTITIONS ADDITIONAL_VENDOR_PROPERTIES += \ ro.boot.dynamic_partitions=$(PRODUCT_USE_DYNAMIC_PARTITIONS) endif ifdef PRODUCT_RETROFIT_DYNAMIC_PARTITIONS ADDITIONAL_VENDOR_PROPERTIES += \ ro.boot.dynamic_partitions_retrofit=$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS) endif ifdef PRODUCT_SHIPPING_API_LEVEL ADDITIONAL_VENDOR_PROPERTIES += \ ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL) endif ifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL # PRODUCT_SHIPPING_VENDOR_API_LEVEL was used to set ro.vendor.api_level # manually for testing. To prevent using this variable for product release, # remove this variable and show an error message. $(error PRODUCT_SHIPPING_VENDOR_API_LEVEL is not available. ro.vendor.api_level\ property must not be set manually) endif ifneq ($(TARGET_BUILD_VARIANT),user) ifdef PRODUCT_SET_DEBUGFS_RESTRICTIONS ADDITIONAL_VENDOR_PROPERTIES += \ ro.product.debugfs_restrictions.enabled=$(PRODUCT_SET_DEBUGFS_RESTRICTIONS) endif endif # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level. # This must not be defined for the non-GRF devices. # The values of the GRF properties will be verified by post_process_props.py ifdef BOARD_SHIPPING_API_LEVEL ADDITIONAL_VENDOR_PROPERTIES += \ ro.board.first_api_level=$(BOARD_SHIPPING_API_LEVEL) endif # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface. # This must not be altered outside of build system. ifdef BOARD_API_LEVEL ADDITIONAL_VENDOR_PROPERTIES += \ ro.board.api_level?=$(BOARD_API_LEVEL) ifdef BOARD_API_LEVEL_PROP_OVERRIDE # This must be used only for testing purpose. Product must not be released # with the modified api level value. $(warning BOARD_API_LEVEL_PROP_OVERRIDE can be defined only for testing purpose) ADDITIONAL_VENDOR_PROPERTIES += \ ro.board.api_level=$(BOARD_API_LEVEL_PROP_OVERRIDE) endif endif # RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen. ifdef RELEASE_BOARD_API_LEVEL_FROZEN ADDITIONAL_VENDOR_PROPERTIES += \ ro.board.api_frozen=$(RELEASE_BOARD_API_LEVEL_FROZEN) endif # Set build prop. This prop is read by ota_from_target_files when generating OTA, # to decide if VABC should be disabled. ifeq ($(BOARD_DONT_USE_VABC_OTA),true) ADDITIONAL_VENDOR_PROPERTIES += \ ro.vendor.build.dont_use_vabc=true endif # Set the flag in vendor. So VTS would know if the new fingerprint format is in use when # the system images are replaced by GSI. ifeq ($(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT),true) ADDITIONAL_VENDOR_PROPERTIES += \ ro.vendor.build.fingerprint_has_digest=1 endif ADDITIONAL_VENDOR_PROPERTIES += \ ro.vendor.build.security_patch=$(VENDOR_SECURITY_PATCH) \ ro.product.board=$(TARGET_BOOTLOADER_BOARD_NAME) \ ro.board.platform=$(TARGET_BOARD_PLATFORM) \ ro.hwui.use_vulkan=$(TARGET_USES_VULKAN) ifdef TARGET_SCREEN_DENSITY ADDITIONAL_VENDOR_PROPERTIES += \ ro.sf.lcd_density=$(TARGET_SCREEN_DENSITY) endif ifdef AB_OTA_UPDATER ADDITIONAL_VENDOR_PROPERTIES += \ ro.build.ab_update=$(AB_OTA_UPDATER) endif ifeq ($(AB_OTA_UPDATER),true) ADDITIONAL_VENDOR_PROPERTIES += ro.vendor.build.ab_ota_partitions=$(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS))) endif user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT)) config_enable_uffd_gc := \ $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default) ADDITIONAL_VENDOR_PROPERTIES := $(strip $(ADDITIONAL_VENDOR_PROPERTIES)) .KATI_READONLY += \ ADDITIONAL_VENDOR_PROPERTIES ================================================ FILE: core/target_test_internal.mk ================================================ ####################################################### ## Shared definitions for all target test compilations. ####################################################### ifeq ($(LOCAL_GTEST),true) LOCAL_CFLAGS += -DGTEST_OS_LINUX_ANDROID -DGTEST_HAS_STD_STRING ifndef LOCAL_SDK_VERSION LOCAL_STATIC_LIBRARIES += libgtest_main libgtest else # TODO(danalbert): Remove the suffix from the module since we only need the # one variant now. my_ndk_gtest_suffix := _c++ LOCAL_STATIC_LIBRARIES += \ libgtest_main_ndk$(my_ndk_gtest_suffix) \ libgtest_ndk$(my_ndk_gtest_suffix) endif endif ifdef LOCAL_MODULE_PATH $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH when building test $(LOCAL_MODULE)) endif ifdef LOCAL_MODULE_PATH_32 $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_32 when building test $(LOCAL_MODULE)) endif ifdef LOCAL_MODULE_PATH_64 $(error $(LOCAL_PATH): Do not set LOCAL_MODULE_PATH_64 when building test $(LOCAL_MODULE)) endif use_testcase_folder := false ifneq ($(LOCAL_MODULE),$(filter $(LOCAL_MODULE),$(DEFAULT_DATA_OUT_MODULES))) use_testcase_folder := true endif ifneq ($(use_testcase_folder),true) ifndef LOCAL_MODULE_RELATIVE_PATH LOCAL_MODULE_RELATIVE_PATH := $(LOCAL_MODULE) endif endif # Implicitly run this test under MTE SYNC for aarch64 binaries. This is a no-op # on non-MTE hardware. ifneq (,$(filter arm64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))) LOCAL_WHOLE_STATIC_LIBRARIES += note_memtag_heap_sync endif ================================================ FILE: core/tasks/README.dex_preopt_check.md ================================================ # `dex_preopt_check` `dex_preopt_check` is a build-time check to make sure that all system server jars are dexpreopted. When the check fails, you will see the following error message: ``` FAILED: build/make/core/tasks/dex_preopt_check.mk:13: warning: Missing compilation artifacts. Dexpreopting is not working for some system server jars Offending entries: ``` Possible causes are: 1. There is an APEX/SDK mismatch. (E.g., the APEX is built from source while the SDK is built from prebuilt.) 1. The `systemserverclasspath_fragment` is not added as `systemserverclasspath_fragments` of the corresponding `apex` module, or not added as `exported_systemserverclasspath_fragments` of the corresponding `prebuilt_apex`/`apex_set` module when building from prebuilt. 1. The expected version of the system server java library is not preferred. (E.g., the `java_import` module has `prefer: false` when building from prebuilt.) 1. Dexpreopting is disabled for the system server java library. This can be due to various reasons including but not limited to: - The java library has `dex_preopt: { enabled: false }` in the Android.bp file. - The java library is listed in `DEXPREOPT_DISABLED_MODULES` in a Makefile. - The java library is missing `installable: true` in the Android.bp file when building from source. - Sanitizer is enabled. 1. `PRODUCT_SYSTEM_SERVER_JARS`, `PRODUCT_APEX_SYSTEM_SERVER_JARS`, `PRODUCT_STANDALONE_SYSTEM_SERVER_JARS`, or `PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` has an extra entry that is not needed by the product. ================================================ FILE: core/tasks/art-host-tests.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: art-host-tests intermediates_dir := $(call intermediates-dir-for,PACKAGING,art-host-tests) art_host_tests_zip := $(PRODUCT_OUT)/art-host-tests.zip # Get the hostside libraries to be packaged in the test zip. Unlike # device-tests.mk or general-tests.mk, the files are not copied to the # testcases directory. my_host_shared_lib_for_art_host_tests := $(foreach f,$(COMPATIBILITY.art-host-tests.HOST_SHARED_LIBRARY.FILES),$(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(_cmf_src))) # Create an artifact to include a list of test config files in art-host-tests. art_host_tests_list_zip := $(PRODUCT_OUT)/art-host-tests_list.zip # Create an artifact to include all test config files in art-host-tests. art_host_tests_configs_zip := $(PRODUCT_OUT)/art-host-tests_configs.zip # Create an artifact to include all shared library files in art-host-tests. art_host_tests_host_shared_libs_zip := $(PRODUCT_OUT)/art-host-tests_host-shared-libs.zip $(art_host_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_art_host_tests) $(art_host_tests_zip) : PRIVATE_art_host_tests_list_zip := $(art_host_tests_list_zip) $(art_host_tests_zip) : PRIVATE_art_host_tests_configs_zip := $(art_host_tests_configs_zip) $(art_host_tests_zip) : PRIVATE_art_host_tests_host_shared_libs_zip := $(art_host_tests_host_shared_libs_zip) $(art_host_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(art_host_tests_list_zip) $(art_host_tests_configs_zip) $(art_host_tests_host_shared_libs_zip) $(art_host_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir) $(art_host_tests_zip) : $(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests) $(SOONG_ZIP) rm -rf $(PRIVATE_INTERMEDIATES_DIR) rm -f $@ $(PRIVATE_art_host_tests_list_zip) mkdir -p $(PRIVATE_INTERMEDIATES_DIR) echo $(sort $(COMPATIBILITY.art-host-tests.FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true $(hide) touch $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \ done $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \ -P host/testcases -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list \ -sha256 grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_configs_zip) \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list grep $(HOST_OUT) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_host_shared_libs_zip) \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/art-host-tests_list $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_art_host_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/art-host-tests_list art-host-tests: $(art_host_tests_zip) $(call dist-for-goals, art-host-tests, $(art_host_tests_zip) $(art_host_tests_list_zip) $(art_host_tests_configs_zip) $(art_host_tests_host_shared_libs_zip)) $(call declare-1p-container,$(art_host_tests_zip),) $(call declare-container-license-deps,$(art_host_tests_zip),$(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests),$(PRODUCT_OUT)/:/) tests: art-host-tests intermediates_dir := art_host_tests_zip := art_host_tests_list_zip := art_host_tests_configs_zip := art_host_tests_host_shared_libs_zip := ================================================ FILE: core/tasks/art.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ######################################################################## # clean-oat rules # .PHONY: clean-oat clean-oat: clean-oat-host clean-oat-target .PHONY: clean-oat-host clean-oat-host: find $(OUT_DIR) '(' -name '*.oat' -o -name '*.odex' -o -name '*.art' -o -name '*.vdex' ')' -a -type f | xargs rm -f rm -rf $(TMPDIR)/*/test-*/dalvik-cache/* rm -rf $(TMPDIR)/android-data/dalvik-cache/* ================================================ FILE: core/tasks/automotive-general-tests.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: automotive-general-tests automotive_general_tests_tools := \ $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \ $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \ $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \ intermediates_dir := $(call intermediates-dir-for,PACKAGING,automotive-general-tests) automotive_general_tests_zip := $(PRODUCT_OUT)/automotive-general-tests.zip # Create an artifact to include a list of test config files in automotive-general-tests. automotive_general_tests_list_zip := $(PRODUCT_OUT)/automotive-general-tests_list.zip # Filter shared entries between automotive-general-tests and automotive-tests's HOST_SHARED_LIBRARY.FILES, # to avoid warning about overriding commands. my_host_shared_lib_for_automotive_general_tests := \ $(foreach m,$(filter $(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES),\ $(COMPATIBILITY.automotive-general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m))) my_automotive_general_tests_shared_lib_files := \ $(filter-out $(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES),\ $(COMPATIBILITY.automotive-general-tests.HOST_SHARED_LIBRARY.FILES)) my_host_shared_lib_for_automotive_general_tests += $(call copy-many-files,$(my_automotive_general_tests_shared_lib_files)) # Create an artifact to include all test config files in automotive-general-tests. automotive_general_tests_configs_zip := $(PRODUCT_OUT)/automotive-general-tests_configs.zip # Create an artifact to include all shared librariy files in automotive-general-tests. automotive_general_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-general-tests_host-shared-libs.zip $(automotive_general_tests_zip) : PRIVATE_automotive_general_tests_list_zip := $(automotive_general_tests_list_zip) $(automotive_general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive_general_tests_list_zip) $(automotive_general_tests_configs_zip) $(automotive_general_tests_host_shared_libs_zip) $(automotive_general_tests_zip) : PRIVATE_TOOLS := $(automotive_general_tests_tools) $(automotive_general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir) $(automotive_general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_general_tests) $(automotive_general_tests_zip) : PRIVATE_automotive_general_tests_configs_zip := $(automotive_general_tests_configs_zip) $(automotive_general_tests_zip) : PRIVATE_general_host_shared_libs_zip := $(automotive_general_tests_host_shared_libs_zip) $(automotive_general_tests_zip) : $(COMPATIBILITY.automotive-general-tests.FILES) $(automotive_general_tests_tools) $(my_host_shared_lib_for_automotive_general_tests) $(SOONG_ZIP) rm -rf $(PRIVATE_INTERMEDIATES_DIR) rm -f $@ $(PRIVATE_automotive_general_tests_list_zip) mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools echo $(sort $(COMPATIBILITY.automotive-general-tests.FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/host.list; \ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \ done grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true cp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/ $(SOONG_ZIP) -d -o $@ \ -P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \ -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list $(SOONG_ZIP) -d -o $(PRIVATE_automotive_general_tests_configs_zip) \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list $(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list $(SOONG_ZIP) -d -o $(PRIVATE_automotive_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/automotive-general-tests_list automotive-general-tests: $(automotive_general_tests_zip) $(call dist-for-goals, automotive-general-tests, $(automotive_general_tests_zip) $(automotive_general_tests_list_zip) $(automotive_general_tests_configs_zip) $(automotive_general_tests_host_shared_libs_zip)) $(call declare-1p-container,$(automotive_general_tests_zip),) $(call declare-container-license-deps,$(automotive_general_tests_zip),$(COMPATIBILITY.automotive-general-tests.FILES) $(automotive_general_tests_tools) $(my_host_shared_lib_for_automotive_general_tests),$(PRODUCT_OUT)/:/) intermediates_dir := automotive_general_tests_tools := automotive_general_tests_zip := automotive_general_tests_list_zip := automotive_general_tests_configs_zip := automotive_general_tests_host_shared_libs_zip := ================================================ FILE: core/tasks/automotive-sdv-tests.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: automotive-sdv-tests automotive-sdv-tests-zip := $(PRODUCT_OUT)/automotive-sdv-tests.zip # Create an artifact to include a list of test config files in automotive-sdv-tests. automotive-sdv-tests-list-zip := $(PRODUCT_OUT)/automotive-sdv-tests_list.zip # Create an artifact to include all test config files in automotive-sdv-tests. automotive-sdv-tests-configs-zip := $(PRODUCT_OUT)/automotive-sdv-tests_configs.zip my_host_shared_lib_for_automotive_sdv_tests := $(call copy-many-files,$(COMPATIBILITY.automotive-sdv-tests.HOST_SHARED_LIBRARY.FILES)) automotive_sdv_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-sdv-tests_host-shared-libs.zip $(automotive-sdv-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip) $(automotive-sdv-tests-zip) : PRIVATE_automotive_sdv_tests_list := $(PRODUCT_OUT)/automotive-sdv-tests_list $(automotive-sdv-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_sdv_tests) $(automotive-sdv-tests-zip) : PRIVATE_automotive_host_shared_libs_zip := $(automotive_sdv_tests_host_shared_libs_zip) $(automotive-sdv-tests-zip) : $(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests) $(SOONG_ZIP) rm -f $@-shared-libs.list echo $(sort $(COMPATIBILITY.automotive-sdv-tests.FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $@-host.list; \ echo $$shared_lib >> $@-shared-libs.list; \ done grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list $(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-configs-zip) \ -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list $(SOONG_ZIP) -d -o $(PRIVATE_automotive_host_shared_libs_zip) \ -P host -C $(HOST_OUT) -l $@-host-shared-libs.list rm -f $(PRIVATE_automotive_sdv_tests_list) $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_automotive_sdv_tests_list) $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_automotive_sdv_tests_list) $(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-list-zip) -C $(dir $@) -f $(PRIVATE_automotive_sdv_tests_list) rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_automotive_sdv_tests_list) automotive-sdv-tests: $(automotive-sdv-tests-zip) $(call dist-for-goals, automotive-sdv-tests, $(automotive-sdv-tests-zip) $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip)) $(call declare-1p-container,$(automotive-sdv-tests-zip),) $(call declare-container-license-deps,$(automotive-sdv-tests-zip),$(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests),$(PRODUCT_OUT)/:/) tests: automotive-sdv-tests ================================================ FILE: core/tasks/automotive-tests.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: automotive-tests automotive-tests-zip := $(PRODUCT_OUT)/automotive-tests.zip # Create an artifact to include a list of test config files in automotive-tests. automotive-tests-list-zip := $(PRODUCT_OUT)/automotive-tests_list.zip # Create an artifact to include all test config files in automotive-tests. automotive-tests-configs-zip := $(PRODUCT_OUT)/automotive-tests_configs.zip my_host_shared_lib_for_automotive_tests := $(call copy-many-files,$(COMPATIBILITY.automotive-tests.HOST_SHARED_LIBRARY.FILES)) automotive_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-tests_host-shared-libs.zip $(automotive-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive-tests-list-zip) $(automotive-tests-configs-zip) $(automotive_tests_host_shared_libs_zip) $(automotive-tests-zip) : PRIVATE_automotive_tests_list := $(PRODUCT_OUT)/automotive-tests_list $(automotive-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_tests) $(automotive-tests-zip) : PRIVATE_automotive_host_shared_libs_zip := $(automotive_tests_host_shared_libs_zip) $(automotive-tests-zip) : $(COMPATIBILITY.automotive-tests.FILES) $(my_host_shared_lib_for_automotive_tests) $(SOONG_ZIP) rm -f $@-shared-libs.list echo $(sort $(COMPATIBILITY.automotive-tests.FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $@-host.list; \ echo $$shared_lib >> $@-shared-libs.list; \ done grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list $(hide) $(SOONG_ZIP) -d -o $(automotive-tests-configs-zip) \ -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list $(SOONG_ZIP) -d -o $(PRIVATE_automotive_host_shared_libs_zip) \ -P host -C $(HOST_OUT) -l $@-host-shared-libs.list rm -f $(PRIVATE_automotive_tests_list) $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_automotive_tests_list) $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_automotive_tests_list) $(hide) $(SOONG_ZIP) -d -o $(automotive-tests-list-zip) -C $(dir $@) -f $(PRIVATE_automotive_tests_list) rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_automotive_tests_list) automotive-tests: $(automotive-tests-zip) $(call dist-for-goals, automotive-tests, $(automotive-tests-zip) $(automotive-tests-list-zip) $(automotive-tests-configs-zip) $(automotive_tests_host_shared_libs_zip)) $(call declare-1p-container,$(automotive-tests-zip),) $(call declare-container-license-deps,$(automotive-tests-zip),$(COMPATIBILITY.automotive-tests.FILES) $(my_host_shared_lib_for_automotive_tests),$(PRODUCT_OUT)/:/) tests: automotive-tests ================================================ FILE: core/tasks/autorepro.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifneq ($(wildcard test/sts/README-autorepro.md),) test_suite_name := autorepro test_suite_tradefed := sts-tradefed test_suite_readme := test/sts/README-autorepro.md autorepro_zip := $(HOST_OUT)/$(test_suite_name)/autorepro.zip include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk autorepro_plugin_skel := $(call intermediates-dir-for,ETC,autorepro-plugin-skel.zip)/autorepro-plugin-skel.zip $(autorepro_zip): AUTOREPRO_ZIP := $(compatibility_zip) $(autorepro_zip): AUTOREPRO_PLUGIN_SKEL := $(autorepro_plugin_skel) $(autorepro_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(autorepro_plugin_skel) rm -f $@ $(AUTOREPRO_ZIP)_filtered $(ZIP2ZIP) -i $(AUTOREPRO_ZIP) -o $(AUTOREPRO_ZIP)_filtered \ -x android-autorepro/tools/sts-tradefed-tests.jar \ 'android-autorepro/tools/*:autorepro/src/main/resources/sts-tradefed-tools/' $(MERGE_ZIPS) $@ $(AUTOREPRO_ZIP)_filtered $(AUTOREPRO_PLUGIN_SKEL) rm -f $(AUTOREPRO_ZIP)_filtered .PHONY: autorepro autorepro: $(autorepro_zip) $(call dist-for-goals, autorepro, $(autorepro_zip)) endif ================================================ FILE: core/tasks/berberis_test.mk ================================================ # # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # BERBERIS_DIR := frameworks/libs/binary_translation # Berberis includes some components which may conflict with other packages. # Only build it when requested explicitly. ifeq ($(BUILD_BERBERIS),true) include $(BERBERIS_DIR)/tests/run_host_tests.mk endif # BUILD_BERBERIS ================================================ FILE: core/tasks/build_custom_images.mk ================================================ # # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Build additional images requested by the product makefile. # This script gives the ability to build multiple additional images and you can # configure what modules/files to include in each image. # 1. Define PRODUCT_CUSTOM_IMAGE_MAKEFILES in your product makefile. # PRODUCT_CUSTOM_IMAGE_MAKEFILES is a list of makefiles. # Each makefile configures an image. # For image configuration makefile foo/bar/xyz.mk, the built image file name # will be xyz.img. So make sure they won't conflict. # 2. In each image's configuration makefile, you can define variables: # - CUSTOM_IMAGE_MOUNT_POINT, the mount point, such as "oem", "odm" etc. # - CUSTOM_IMAGE_PARTITION_SIZE # - CUSTOM_IMAGE_FILE_SYSTEM_TYPE # - CUSTOM_IMAGE_DICT_FILE, a text file defines a dictionary accepted by # BuildImage() in tools/releasetools/build_image.py. # - CUSTOM_IMAGE_MODULES, a list of module names you want to include in # the image; Not only the module itself will be installed to proper path in # the image, you can also piggyback additional files/directories with the # module's LOCAL_PICKUP_FILES. # - CUSTOM_IMAGE_COPY_FILES, a list of ":" to be copied to the # image. is relativ to the root of the image. # - CUSTOM_IMAGE_SELINUX, set to "true" if the image supports selinux. # - CUSTOM_IMAGE_SUPPORT_VERITY, set to "true" if the product supports verity. # - CUSTOM_IMAGE_SUPPORT_VERITY_FEC, set to "true" if the product supports # verity FEC (forward error correction). # - CUSTOM_IMAGE_VERITY_BLOCK_DEVICE # - CUSTOM_IMAGE_AVB_HASH_ENABLE, set to "true" to add AVB HASH footer. # - CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS, additional args of AVB HASH footer. # - CUSTOM_IMAGE_AVB_HASHTREE_ENABLE, set to "true" to add AVB HASHTREE # footer. # - CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS, additional args of AVB # HASHTREE footer. # - CUSTOM_IMAGE_AVB_KEY_PATH, custom AVB signing key. # - CUSTOM_IMAGE_AVB_ALGORITHM, custom AVB signing algorithm. # # To build all those images, run "make custom_images". ifneq ($(filter $(MAKECMDGOALS),custom_images),) .PHONY: custom_images custom_image_parameter_variables := \ CUSTOM_IMAGE_MOUNT_POINT \ CUSTOM_IMAGE_PARTITION_SIZE \ CUSTOM_IMAGE_FILE_SYSTEM_TYPE \ CUSTOM_IMAGE_DICT_FILE \ CUSTOM_IMAGE_MODULES \ CUSTOM_IMAGE_COPY_FILES \ CUSTOM_IMAGE_SELINUX \ CUSTOM_IMAGE_VERITY_BLOCK_DEVICE \ CUSTOM_IMAGE_AVB_HASH_ENABLE \ CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS \ CUSTOM_IMAGE_AVB_HASHTREE_ENABLE \ CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS \ CUSTOM_IMAGE_AVB_KEY_PATH \ CUSTOM_IMAGE_AVB_ALGORITHM \ # We don't expect product makefile to inherit/override PRODUCT_CUSTOM_IMAGE_MAKEFILES, # so we don't put it in the _product_var_list. $(foreach mk, $(PRODUCT_CUSTOM_IMAGE_MAKEFILES),\ $(eval my_custom_imag_makefile := $(mk))\ $(eval include $(BUILD_SYSTEM)/tasks/tools/build_custom_image.mk)) endif ================================================ FILE: core/tasks/catbox.mk ================================================ # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. test_suite_name := catbox test_suite_tradefed := catbox-tradefed test_suite_readme := test/catbox/tools/catbox-tradefed/README test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/catbox-report-lib.jar include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: catbox catbox: $(compatibility_zip) $(call dist-for-goals, catbox, $(compatibility_zip)) ================================================ FILE: core/tasks/check-abi-dump-list.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ##################################################################### # Check the generate list against the latest list stored in the # source tree .PHONY: check-abi-dump-list # Check if vndk list is changed droidcore: check-abi-dump-list check-abi-dump-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-abi-dump-list-timestamp # The ABI tool does not support sanitizer and coverage builds. ifeq (,$(filter true,$(SKIP_ABI_CHECKS) $(CLANG_COVERAGE))) ifeq (,$(SANITIZE_TARGET)) check-abi-dump-list: $(check-abi-dump-list-timestamp) endif endif ##################################################################### # ABI reference dumps. # LSDUMP_PATHS is a list of tag:path. They are written to LSDUMP_PATHS_FILE. LSDUMP_PATHS_FILE := $(PRODUCT_OUT)/lsdump_paths.txt $(LSDUMP_PATHS_FILE): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS) $(LSDUMP_PATHS_FILE): @echo "Generate $@" @rm -rf $@ && echo -e "$(subst :,:$(space),$(subst $(space),\n,$(PRIVATE_LSDUMP_PATHS)))" > $@ # $(1): A list of tags. # $(2): A list of tag:path. # Return the file paths of the ABI dumps that match the tags. define filter-abi-dump-paths $(eval tag_patterns := $(addsuffix :%,$(1))) $(patsubst $(tag_patterns),%,$(filter $(tag_patterns),$(2))) endef # Subsets of LSDUMP_PATHS. .PHONY: findlsdumps_APEX findlsdumps_APEX: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,APEX,$(LSDUMP_PATHS)) .PHONY: findlsdumps_LLNDK findlsdumps_LLNDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,LLNDK,$(LSDUMP_PATHS)) .PHONY: findlsdumps_NDK findlsdumps_NDK: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,NDK,$(LSDUMP_PATHS)) .PHONY: findlsdumps_PLATFORM findlsdumps_PLATFORM: $(LSDUMP_PATHS_FILE) $(call filter-abi-dump-paths,PLATFORM,$(LSDUMP_PATHS)) .PHONY: findlsdumps findlsdumps: $(LSDUMP_PATHS_FILE) $(foreach p,$(LSDUMP_PATHS),$(call word-colon,2,$(p))) ##################################################################### # Check that all ABI reference dumps have corresponding # APEX/LLNDK/PLATFORM libraries. # $(1): The directory containing ABI dumps. # Return a list of ABI dump paths ending with .so.lsdump. define find-abi-dump-paths $(if $(wildcard $(1)), \ $(addprefix $(1)/, \ $(call find-files-in-subdirs,$(1),"*.so.lsdump" -and -type f,.))) endef # $(1): A list of tags. # $(2): A list of tag:path. # Return the file names of the ABI dumps that match the tags, and replace the # file name extensions with .so.lsdump. define filter-abi-dump-names $(patsubst %.so.llndk.lsdump,%.so.lsdump, \ $(patsubst %.so.apex.lsdump,%.so.lsdump, \ $(notdir $(call filter-abi-dump-paths,$(1),$(2))))) endef VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(RELEASE_BOARD_API_LEVEL) ifeq (REL,$(PLATFORM_VERSION_CODENAME)) PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_SDK_VERSION) else PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/current endif VNDK_ABI_DUMPS := $(call find-abi-dump-paths,$(VNDK_ABI_DUMP_DIR)) PLATFORM_ABI_DUMPS := $(call find-abi-dump-paths,$(PLATFORM_ABI_DUMP_DIR)) # Check for superfluous lsdump files. Since LSDUMP_PATHS only covers the # libraries that can be built from source in the current build, and prebuilts of # Mainline modules may be in use, we also allow the libs in STUB_LIBRARIES for # platform ABIs. # In addition, libRS is allowed because it's disabled for RISC-V. $(check-abi-dump-list-timestamp): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS) $(check-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES) $(check-abi-dump-list-timestamp): $(eval added_vndk_abi_dumps := $(strip $(sort $(filter-out \ $(call filter-abi-dump-names,LLNDK,$(PRIVATE_LSDUMP_PATHS)) libRS.so.lsdump, \ $(notdir $(VNDK_ABI_DUMPS)))))) $(if $(added_vndk_abi_dumps), \ echo -e "Found unexpected ABI reference dump files under $(VNDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(VNDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_vndk_abi_dumps)) ')' -delete\` to delete the dump files.") # TODO(b/314010764): Remove LLNDK tag after PLATFORM_SDK_VERSION is upgraded to 35. $(eval added_platform_abi_dumps := $(strip $(sort $(filter-out \ $(call filter-abi-dump-names,APEX LLNDK PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \ $(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)) libRS.so.lsdump, \ $(notdir $(PLATFORM_ABI_DUMPS)))))) $(if $(added_platform_abi_dumps), \ echo -e "Found unexpected ABI reference dump files under $(PLATFORM_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(PLATFORM_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_platform_abi_dumps)) ')' -delete\` to delete the dump files.") $(if $(added_vndk_abi_dumps)$(added_platform_abi_dumps),exit 1) $(hide) mkdir -p $(dir $@) $(hide) touch $@ ================================================ FILE: core/tasks/csuite.mk ================================================ # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. test_suite_name := csuite test_suite_tradefed := csuite-tradefed test_suite_readme := test/app_compat/csuite/README.md include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: csuite csuite: $(compatibility_zip) $(call dist-for-goals, csuite, $(compatibility_zip)) ================================================ FILE: core/tasks/cts.mk ================================================ # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. test_suite_name := cts test_suite_tradefed := cts-tradefed test_suite_dynamic_config := cts/tools/cts-tradefed/DynamicConfig.xml test_suite_readme := cts/tools/cts-tradefed/README test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \ $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar $(call declare-1p-target,$(test_suite_dynamic_config),cts) $(call declare-1p-target,$(test_suite_readme),cts) include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: cts cts: $(compatibility_zip) $(compatibility_tests_list_zip) $(call dist-for-goals, cts, $(compatibility_zip) $(compatibility_tests_list_zip)) .PHONY: cts_v2 cts_v2: cts # platform version check (b/32056228) # ============================================================ ifneq (,$(wildcard cts/)) cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt cts_platform_version_string := $(shell cat $(cts_platform_version_path)) cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt cts_platform_release_string := $(shell cat $(cts_platform_release_path)) ifneq (REL,$(PLATFORM_VERSION_CODENAME)) ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string))) define error_msg ============================================================ Could not find version "$(PLATFORM_VERSION)" in CTS platform version file: $(cts_platform_version_path) Most likely PLATFORM_VERSION in build/core/version_defaults.mk has changed and a new version must be added to this CTS file. ============================================================ endef $(error $(error_msg)) endif endif ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string))) define error_msg ============================================================ Could not find version "$(PLATFORM_VERSION_LAST_STABLE)" in CTS platform release file: $(cts_platform_release_path) Most likely PLATFORM_VERSION_LAST_STABLE in build/core/version_defaults.mk has changed and a new version must be added to this CTS file. ============================================================ endef $(error $(error_msg)) endif endif # Creates a "cts-verifier" directory that will contain: # # 1. Out directory with a "android-cts-verifier" containing the CTS Verifier # and other binaries it needs. # # 2. Zipped version of the android-cts-verifier directory to be included with # the build distribution. ## cts-dir := $(HOST_OUT)/cts-verifier verifier-dir-name := android-cts-verifier verifier-dir := $(cts-dir)/$(verifier-dir-name) verifier-zip-name := $(verifier-dir-name).zip verifier-zip := $(cts-dir)/$(verifier-zip-name) cts : $(verifier-zip) $(verifier-zip): PRIVATE_DIR := $(cts-dir) $(verifier-zip): $(SOONG_ANDROID_CTS_VERIFIER_ZIP) rm -rf $(PRIVATE_DIR) mkdir -p $(PRIVATE_DIR) unzip -q -d $(PRIVATE_DIR) $< $(copy-file-to-target) # For producing CTS coverage reports. # Run "make cts-test-coverage" in the $ANDROID_BUILD_TOP directory. cts_api_coverage_exe := $(HOST_OUT_EXECUTABLES)/cts-api-coverage dexdeps_exe := $(HOST_OUT_EXECUTABLES)/dexdeps cts_api_map_exe := $(HOST_OUT_EXECUTABLES)/cts-api-map coverage_out := $(HOST_OUT)/cts-api-coverage api_map_out := $(HOST_OUT)/cts-api-map cts_jar_files := $(api_map_out)/cts_jar_files.txt cts_v_host_jar_files := $(api_map_out)/cts_v_host_jar_files.txt cts_all_jar_files := $(api_map_out)/cts_all_jar_files.txt $(cts_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES)) $(cts_jar_files): mkdir -p $(dir $@) echo $(PRIVATE_API_MAP_FILES) > $@ $(cts_v_host_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts-v-host.API_MAP_FILES)) $(cts_v_host_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST) mkdir -p $(dir $@) cp $< $@ echo $(PRIVATE_API_MAP_FILES) >> $@ $(cts_all_jar_files): PRIVATE_API_MAP_FILES := $(sort $(COMPATIBILITY.cts.API_MAP_FILES) \ $(COMPATIBILITY.cts-v-host.API_MAP_FILES)) $(cts_all_jar_files): $(SOONG_ANDROID_CTS_VERIFIER_APP_LIST) mkdir -p $(dir $@) cp $< $@ echo $(PRIVATE_API_MAP_FILES) >> $@ api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml napi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml napi_xml_description := $(coverage_out)/ndk-api.xml $(napi_xml_description) : $(napi_text_description) $(ACP) $(hide) echo "Preparing NDK API XML: $@" $(hide) mkdir -p $(dir $@) $(hide) $(ACP) $< $@ system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml module_lib_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/module-lib-api.xml system_service_api_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-server-api.xml combined_api_xml_description := $(api_xml_description) \ $(system_api_xml_description) \ $(module_lib_api_xml_description) \ $(system_service_api_description) cts-test-coverage-report := $(coverage_out)/test-coverage.html cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html cts-combined-coverage-report := $(coverage_out)/combined-coverage.html cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description) cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description) cts-api-map-xml-report := $(api_map_out)/cts-api-map.xml cts-v-host-api-map-xml-report := $(api_map_out)/cts-v-host-api-map.xml cts-combined-api-map-xml-report := $(api_map_out)/cts-combined-api-map.xml cts-combined-api-map-html-report := $(api_map_out)/cts-combined-api-map.html cts-combined-api-inherit-xml-report := $(api_map_out)/cts-combined-api-inherit.xml cts_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_jar_files) cts_v_host_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_v_host_jar_files) cts_combined_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_all_jar_files) android_cts_zip := $(HOST_OUT)/cts/android-cts.zip cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk $(cts-test-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) $(cts-test-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-test-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-test-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) $(cts-test-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) $(cts-test-coverage-report) : $(android_cts_zip) $(cts_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\ $(PRIVATE_TEST_CASES),html) $(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) $(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description) $(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := "" $(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS System API Coverage Report",\ $(PRIVATE_TEST_CASES),html) $(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts) $(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description) $(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := "" $(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\ $(PRIVATE_TEST_CASES),xml) $(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(verifier-dir), $(c)) $(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) $(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) $(cts-verifier-coverage-report) : $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS Verifier API Coverage Report",\ $(PRIVATE_TEST_CASES),html) $(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c)) $(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) $(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) $(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS Combined API Coverage Report",\ $(PRIVATE_TEST_CASES),html) $(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c)) $(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe) $(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe) $(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description) $(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description) $(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP) $(call generate-coverage-report-cts,"CTS Combined API Coverage Report - XML",\ $(PRIVATE_TEST_CASES),xml) .PHONY: cts-test-coverage cts-test-coverage : $(cts-test-coverage-report) .PHONY: cts-system-api-coverage cts-system-api-coverage : $(cts-system-api-coverage-report) .PHONY: cts-system-api-xml-coverage cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report) .PHONY: cts-verifier-coverage cts-verifier-coverage : $(cts-verifier-coverage-report) .PHONY: cts-combined-coverage cts-combined-coverage : $(cts-combined-coverage-report) .PHONY: cts-combined-xml-coverage cts-combined-xml-coverage : $(cts-combined-xml-coverage-report) .PHONY: cts-coverage-report-all cts-api-coverage cts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage $(cts-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe) $(cts-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description) $(cts-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_jar_files) $(cts-api-map-xml-report) : $(android_cts_zip) $(cts_api_map_dependencies) | $(ACP) $(call generate-api-map-report-cts,"CTS API MAP Report - XML",\ $(PRIVATE_JAR_FILES),xml) $(cts-v-host-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe) $(cts-v-host-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description) $(cts-v-host-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_v_host_jar_files) $(cts-v-host-api-map-xml-report) : $(verifier_zip) $(cts_v_host_api_map_dependencies) | $(ACP) $(call generate-api-map-report-cts,"CTS-V-HOST API MAP Report - XML",\ $(PRIVATE_JAR_FILES),xml) $(cts-combined-api-map-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe) $(cts-combined-api-map-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description) $(cts-combined-api-map-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files) $(cts-combined-api-map-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP) $(call generate-api-map-report-cts,"CTS Combined API MAP Report - XML",\ $(PRIVATE_JAR_FILES),xml) $(cts-combined-api-map-html-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe) $(cts-combined-api-map-html-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description) $(cts-combined-api-map-html-report): PRIVATE_JAR_FILES := $(cts_all_jar_files) $(cts-combined-api-map-html-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP) $(call generate-api-map-report-cts,"CTS Combined API MAP Report - HTML",\ $(PRIVATE_JAR_FILES),html) $(cts-combined-api-inherit-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe) $(cts-combined-api-inherit-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description) $(cts-combined-api-inherit-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files) $(cts-combined-api-inherit-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP) $(call generate-api-inherit-report-cts,"CTS Combined API Inherit Report - XML",\ $(PRIVATE_JAR_FILES),xml) .PHONY: cts-api-map-xml cts-api-map-xml : $(cts-api-map-xml-report) .PHONY: cts-v-host-api-map-xml cts-v-host-api-map-xml: $(cts-v-host-api-map-xml-report) .PHONY: cts-combined-api-map-xml cts-combined-api-map-xml : $(cts-combined-api-map-xml-report) .PHONY: cts-combined-api-inherit-xml cts-combined-api-inherit-xml : $(cts-combined-api-inherit-xml-report) .PHONY: cts-api-map-all # Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals. $(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml) $(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml) ALL_TARGETS.$(cts-test-coverage-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-system-api-coverage-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-system-api-xml-coverage-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-verifier-coverage-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-combined-coverage-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-combined-xml-coverage-report).META_LIC:=$(module_license_metadata) # Put the test api map report in the dist dir if "cts-api-map-all" is among the build goals. $(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-xml-report):cts-api-map-report.xml) $(call dist-for-goals, cts-api-map-all, $(cts-combined-api-inherit-xml-report):cts-api-inherit-report.xml) ALL_TARGETS.$(cts-api-map-xml-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-v-host-api-map-xml-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-combined-api-map-xml-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-combined-api-map-html-report).META_LIC:=$(module_license_metadata) ALL_TARGETS.$(cts-combined-api-map-inherit-report).META_LIC:=$(module_license_metadata) # Arguments; # 1 - Name of the report printed out on the screen # 2 - List of apk files that will be scanned to generate the report # 3 - Format of the report define generate-coverage-report-cts $(hide) mkdir -p $(dir $@) $(hide) $(PRIVATE_CTS_API_COVERAGE_EXE) -j 8 -d $(PRIVATE_DEXDEPS_EXE) -a $(PRIVATE_API_XML_DESC) -n $(PRIVATE_NAPI_XML_DESC) -f $(3) -o $@ $(2) @ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@) endef # Arguments; # 1 - Name of the report printed out on the screen # 2 - A file containing list of files that to be analyzed # 3 - Format of the report define generate-api-map-report-cts $(hide) mkdir -p $(dir $@) $(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m api_map -m xts_annotation -a $(shell echo "$(PRIVATE_API_XML_DESC)" | tr ' ' ',') -i $(2) -f $(3) -o $@ @ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@) endef # Arguments; # 1 - Name of the report printed out on the screen # 2 - A file containing list of files that to be analyzed # 3 - Format of the report define generate-api-inherit-report-cts $(hide) mkdir -p $(dir $@) $(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m xts_api_inherit -a $(shell echo "$(PRIVATE_API_XML_DESC)" | tr ' ' ',') -i $(2) -f $(3) -o $@ @ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@) endef # Reset temp vars cts_api_coverage_dependencies := cts_system_api_coverage_dependencies := cts_api_map_dependencies := cts_v_host_api_map_dependencies := cts_combined_api_map_dependencies := cts-combined-coverage-report := cts-combined-xml-coverage-report := cts-verifier-coverage-report := cts-test-coverage-report := cts-system-api-coverage-report := cts-system-api-xml-coverage-report := cts-api-map-xml-report := cts-v-host-api-map-xml-report := cts-combined-api-map-xml-report := cts-combined-api-map-html-report := cts-combined-api-map-inherit-report := api_xml_description := api_text_description := system_api_xml_description := combined_api_xml_description := napi_xml_description := napi_text_description := coverage_out := api_map_out := cts_jar_files := dexdeps_exe := cts_api_coverage_exe := cts_api_map_exe := cts_verifier_apk := android_cts_zip := cts-dir := verifier-dir-name := verifier-dir := verifier-zip-name := verifier-zip := ================================================ FILE: core/tasks/cts_root.mk ================================================ # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifneq ($(wildcard test/cts-root/README.md),) test_suite_name := cts_root test_suite_tradefed := cts-root-tradefed test_suite_readme := test/cts-root/README.md include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: cts_root cts_root: $(compatibility_zip) $(call dist-for-goals, cts_root, $(compatibility_zip)) endif ================================================ FILE: core/tasks/device-platinum-tests.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: device-platinum-tests device_platinum_tests_zip := $(PRODUCT_OUT)/device-platinum-tests.zip # Create an artifact to include a list of test config files in device-platinum-tests. device_platinum_tests_list_zip := $(PRODUCT_OUT)/device-platinum-tests_list.zip # Create an artifact to include all test config files in device-platinum-tests. device_platinum_tests_configs_zip := $(PRODUCT_OUT)/device-platinum-tests_configs.zip my_host_shared_lib_for_device_platinum_tests := $(call copy-many-files,$(COMPATIBILITY.device-platinum-tests.HOST_SHARED_LIBRARY.FILES)) device_platinum_tests_host_shared_libs_zip := $(PRODUCT_OUT)/device-platinum-tests_host-shared-libs.zip $(device_platinum_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(device_platinum_tests_list_zip) $(device_platinum_tests_configs_zip) $(device_platinum_tests_host_shared_libs_zip) $(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_list_zip := $(device_platinum_tests_list_zip) $(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_configs_zip := $(device_platinum_tests_configs_zip) $(device_platinum_tests_zip) : PRIVATE_device_platinum_tests_list := $(PRODUCT_OUT)/device-platinum-tests_list $(device_platinum_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_platinum_tests) $(device_platinum_tests_zip) : PRIVATE_device_host_shared_libs_zip := $(device_platinum_tests_host_shared_libs_zip) $(device_platinum_tests_zip) : $(COMPATIBILITY.device-platinum-tests.FILES) $(my_host_shared_lib_for_device_platinum_tests) $(SOONG_ZIP) rm -f $@-shared-libs.list rm -f $(PRIVATE_device_platinum_tests_list_zip) echo $(sort $(COMPATIBILITY.device-platinum-tests.FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $@-host.list; \ echo $$shared_lib >> $@-shared-libs.list; \ done grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256 $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_device_platinum_tests_configs_zip) \ -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list $(SOONG_ZIP) -d -o $(PRIVATE_device_host_shared_libs_zip) \ -P host -C $(HOST_OUT) -l $@-host-shared-libs.list rm -f $(PRIVATE_device_platinum_tests_list) $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_platinum_tests_list) $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_platinum_tests_list) $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_device_platinum_tests_list_zip) -C $(dir $@) -f $(PRIVATE_device_platinum_tests_list) rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_device_platinum_tests_list) device-platinum-tests: $(device_platinum_tests_zip) $(call dist-for-goals, device-platinum-tests, $(device_platinum_tests_zip) $(device_platinum_tests_list_zip) $(device_platinum_tests_configs_zip) $(device_platinum_tests_host_shared_libs_zip)) $(call declare-1p-container,$(device_platinum_tests_zip),) $(call declare-container-license-deps,$(device_platinum_tests_zip),$(COMPATIBILITY.device-platinum-tests.FILES) $(my_host_shared_lib_for_device_platinum_tests),$(PRODUCT_OUT)/:/) tests: device-platinum-tests # Reset temp vars device_platinum_tests_zip := device_platinum_tests_list_zip := device_platinum_tests_configs_zip := my_host_shared_lib_for_device_platinum_tests := device_platinum_tests_host_shared_libs_zip := ================================================ FILE: core/tasks/device-tests.mk ================================================ # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: device-tests .PHONY: device-tests-files-list device-tests-zip := $(PRODUCT_OUT)/device-tests.zip # Create an artifact to include a list of test config files in device-tests. device-tests-list-zip := $(PRODUCT_OUT)/device-tests_list.zip # Create an artifact to include all test config files in device-tests. device-tests-configs-zip := $(PRODUCT_OUT)/device-tests_configs.zip my_host_shared_lib_for_device_tests := $(call copy-many-files,$(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES)) device_tests_files_list := $(PRODUCT_OUT)/device-tests_files $(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip) $(device-tests-zip) : PRIVATE_device_tests_list := $(PRODUCT_OUT)/device-tests_list $(device-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests) $(device-tests-zip) : $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(my_host_shared_lib_for_device_tests) $(SOONG_ZIP) echo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $@-host.list; \ done grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256 $(hide) $(SOONG_ZIP) -d -o $(device-tests-configs-zip) \ -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list rm -f $(PRIVATE_device_tests_list) $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_device_tests_list) $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_device_tests_list) $(hide) $(SOONG_ZIP) -d -o $(device-tests-list-zip) -C $(dir $@) -f $(PRIVATE_device_tests_list) rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ $(PRIVATE_device_tests_list) $(device_tests_files_list) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests) $(device_tests_files_list) : echo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@.full_list grep $(HOST_OUT_TESTCASES) $@.full_list > $@ || true grep $(TARGET_OUT_TESTCASES) $@.full_list >> $@ || true device-tests: $(device-tests-zip) device-tests-files-list: $(device_tests_files_list) $(call dist-for-goals, device-tests, $(device-tests-zip) $(device-tests-list-zip) $(device-tests-configs-zip)) $(call declare-1p-container,$(device-tests-zip),) $(call declare-container-license-deps,$(device-tests-zip),$(COMPATIBILITY.device-tests.FILES) $(my_host_shared_lib_for_device_tests),$(PRODUCT_OUT)/:/) tests: device-tests ================================================ FILE: core/tasks/dex_preopt_check.mk ================================================ # Checks that some critical dexpreopt output files are installed. # Inputs: # DISABLE_DEXPREOPT_CHECK: True if the check should be disabled. # PRODUCT_PACKAGES: The list of packages to be installed for the product. # ALL_DEFAULT_INSTALLED_MODULES: The full list of modules going to be installed. # DEXPREOPT_SYSTEMSERVER_ARTIFACTS: The list of compilation artifacts of system server jars, which # is generated by Soong in dexpreopt_check.go. ifneq (true,$(DISABLE_DEXPREOPT_CHECK)) # Skip the check if the system server is not installed for the product. ifneq (,$(filter services,$(PRODUCT_PACKAGES))) $(call maybe-print-list-and-error,\ $(filter-out $(ALL_DEFAULT_INSTALLED_MODULES),$(DEXPREOPT_SYSTEMSERVER_ARTIFACTS)),\ Missing compilation artifacts. Dexpreopting is not working for some system server jars. See \ https://cs.android.com/android/platform/superproject/+/master:build/make/core/tasks/README.dex_preopt_check.md \ ) endif endif ================================================ FILE: core/tasks/dts.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Desktop test suite ifneq ($(wildcard test/dts/tools/dts-tradefed/README),) test_suite_name := dts test_suite_tradefed := dts-tradefed test_suite_readme := test/dts/tools/dts-tradefed/README test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \ $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: dts dts: $(compatibility_zip) $(compatibility_tests_list_zip) $(call dist-for-goals, dts, $(compatibility_zip) $(compatibility_tests_list_zip)) endif ================================================ FILE: core/tasks/find-shareduid-violation.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # shareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json $(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation $(shareduid_violation_modules_filename): $(AAPT2) $(HOST_OUT_EXECUTABLES)/find_shareduid_violation \ --product_out $(PRODUCT_OUT) \ --aapt $(AAPT2) \ --copy_out_system $(TARGET_COPY_OUT_SYSTEM) \ --copy_out_vendor $(TARGET_COPY_OUT_VENDOR) \ --copy_out_product $(TARGET_COPY_OUT_PRODUCT) \ --copy_out_system_ext $(TARGET_COPY_OUT_SYSTEM_EXT) \ > $@ $(call declare-0p-target,$(shareduid_violation_modules_filename)) $(call dist-for-goals,droidcore,$(shareduid_violation_modules_filename)) ================================================ FILE: core/tasks/fontchain_lint.mk ================================================ # Copyright (C) 2011 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Run sanity tests on fonts on checkbuild checkbuild: fontchain_lint FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter ifeq ($(MINIMAL_FONT_FOOTPRINT),true) CHECK_EMOJI := false else CHECK_EMOJI := true endif fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp .PHONY: fontchain_lint fontchain_lint: $(fontchain_lint_timestamp) fontchain_lint_deps := \ external/unicode/DerivedAge.txt \ external/unicode/emoji-data.txt \ external/unicode/emoji-sequences.txt \ external/unicode/emoji-variation-sequences.txt \ external/unicode/emoji-zwj-sequences.txt \ external/unicode/additions/emoji-data.txt \ external/unicode/additions/emoji-sequences.txt \ external/unicode/additions/emoji-zwj-sequences.txt \ $(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps) @echo Running fontchain lint $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode touch $@ ================================================ FILE: core/tasks/general-tests.mk ================================================ # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: general-tests .PHONY: general-tests-files-list general_tests_tools := \ $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \ $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \ $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \ intermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests) general_tests_zip := $(PRODUCT_OUT)/general-tests.zip # Create an artifact to include a list of test config files in general-tests. general_tests_list_zip := $(PRODUCT_OUT)/general-tests_list.zip # Create an artifact to include all test config files in general-tests. general_tests_configs_zip := $(PRODUCT_OUT)/general-tests_configs.zip # Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES, # to avoid warning about overriding commands. my_host_shared_lib_for_general_tests := \ $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m))) my_general_tests_shared_lib_files := \ $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)) my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files)) my_host_shared_lib_symlinks := \ $(filter $(COMPATIBILITY.host-unit-tests.SYMLINKS),\ $(COMPATIBILITY.general-tests.SYMLINKS)) my_general_tests_symlinks := \ $(filter-out $(COMPATIBILITY.camera-hal-tests.SYMLINKS),\ $(filter-out $(COMPATIBILITY.host-unit-tests.SYMLINKS),\ $(COMPATIBILITY.general-tests.SYMLINKS))) my_symlinks_for_general_tests := $(foreach f,$(my_general_tests_symlinks),\ $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \ $(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \ $(_cmf_dest))) # In this one directly take the overlap into the zip since we can't rewrite rules my_symlinks_for_general_tests += $(foreach f,$(my_host_shared_lib_symlinks),\ $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \ $(_cmf_dest))) general_tests_files_list := $(PRODUCT_OUT)/general-tests_files general_tests_host_files_list := $(PRODUCT_OUT)/general-tests_host_files general_tests_target_files_list := $(PRODUCT_OUT)/general-tests_target_files $(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip) $(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools) $(general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir) $(general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests) $(general_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_general_tests) $(general_tests_zip) : PRIVATE_general_tests_configs_zip := $(general_tests_configs_zip) $(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(my_host_shared_lib_for_general_tests) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(general_tests_tools) $(my_symlinks_for_general_tests) $(SOONG_ZIP) rm -rf $(PRIVATE_INTERMEDIATES_DIR) rm -f $@ $(PRIVATE_general_tests_list_zip) mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools echo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list for symlink in $(PRIVATE_SYMLINKS); do \ echo $$symlink >> $(PRIVATE_INTERMEDIATES_DIR)/list; \ done $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \ done grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list > $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list || true cp -fp $(PRIVATE_TOOLS) $(PRIVATE_INTERMEDIATES_DIR)/tools/ $(SOONG_ZIP) -d -o $@ \ -P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \ -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list \ -sha256 $(SOONG_ZIP) -d -o $(PRIVATE_general_tests_configs_zip) \ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target-test-configs.list grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list $(SOONG_ZIP) -d -o $(PRIVATE_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list $(general_tests_files_list) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir) $(general_tests_files_list) : PRIVATE_general_tests_host_files_list := $(general_tests_host_files_list) $(general_tests_files_list) : PRIVATE_general_tests_target_files_list := $(general_tests_target_files_list) $(general_tests_files_list) : echo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@ grep $(HOST_OUT_TESTCASES) $@ > $(PRIVATE_general_tests_host_files_list) || true grep $(TARGET_OUT_TESTCASES) $@ >> $(PRIVATE_general_tests_target_files_list) || true general-tests: $(general_tests_zip) general-tests-files-list: $(general_tests_files_list) $(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_shared_libs_zip)) $(call declare-1p-container,$(general_tests_zip),) $(call declare-container-license-deps,$(general_tests_zip),$(COMPATIBILITY.general-tests.FILES) $(general_tests_tools),$(PRODUCT_OUT)/:/) intermediates_dir := general_tests_tools := general_tests_zip := general_tests_list_zip := general_tests_configs_zip := general_tests_shared_libs_zip := my_host_shared_lib_for_general_tests := my_symlinks_for_general_tests := my_general_tests_shared_lib_files := my_general_tests_symlinks := my_host_shared_lib_symlinks := ================================================ FILE: core/tasks/host-unit-tests.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # `host-unit-tests` shall only include hostside unittest that don't require a device to run. Tests # included will be run as part of presubmit check. # To add tests to the suite, do one of the following: # * For test modules configured with Android.bp, set attribute `test_options: { unit_test: true }` # * For test modules configured with mk, set `LOCAL_IS_UNIT_TEST := true` .PHONY: host-unit-tests intermediates_dir := $(call intermediates-dir-for,PACKAGING,host-unit-tests) host_unit_tests_zip := $(PRODUCT_OUT)/host-unit-tests.zip # Get the hostside libraries to be packaged in the test zip. Unlike # device-tests.mk or general-tests.mk, the files are not copied to the # testcases directory. my_host_shared_lib_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.HOST_SHARED_LIBRARY.FILES),$(strip \ $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \ $(_cmf_src))) my_symlinks_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.SYMLINKS),\ $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \ $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \ $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \ $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \ $(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \ $(_cmf_dest))) $(host_unit_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_host_unit_tests) $(host_unit_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_host_unit_tests) $(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(my_symlinks_for_host_unit_tests) $(SOONG_ZIP) echo $(sort $(COMPATIBILITY.host-unit-tests.FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true echo "" >> $@-host-libs.list $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \ echo $$shared_lib >> $@-host-libs.list; \ done $(hide) for symlink in $(PRIVATE_SYMLINKS); do \ echo $$symlink >> $@-host.list; \ done grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \ -P target -C $(PRODUCT_OUT) -l $@-target.list \ -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list -sha256 rm -f $@.list $@-host.list $@-target.list $@-host-libs.list host-unit-tests: $(host_unit_tests_zip) $(call dist-for-goals, host-unit-tests, $(host_unit_tests_zip)) $(call declare-1p-container,$(host_unit_tests_zip),) $(call declare-container-license-deps,$(host_unit_tests_zip),$(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests),$(PRODUCT_OUT)/:/) tests: host-unit-tests ================================================ FILE: core/tasks/host_init_verifier.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # host_init_verifier_output := $(PRODUCT_OUT)/host_init_verifier_output.txt $(host_init_verifier_output): \ $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ $(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ $(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ $(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ $(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ $(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts # Run host_init_verifier on the partition staging directories. $(host_init_verifier_output): $(HOST_INIT_VERIFIER) $(HOST_INIT_VERIFIER) \ -p $(call intermediates-dir-for,ETC,passwd_system)/passwd_system \ -p $(call intermediates-dir-for,ETC,passwd_system_ext)/passwd_system_ext \ -p $(call intermediates-dir-for,ETC,passwd_vendor)/passwd_vendor \ -p $(call intermediates-dir-for,ETC,passwd_odm)/passwd_odm \ -p $(call intermediates-dir-for,ETC,passwd_product)/passwd_product \ --property-contexts=$(call intermediates-dir-for,ETC,plat_property_contexts)/plat_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,system_ext_property_contexts)/system_ext_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,product_property_contexts)/product_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,vendor_property_contexts)/vendor_property_contexts \ --property-contexts=$(call intermediates-dir-for,ETC,odm_property_contexts)/odm_property_contexts \ --out_system $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) \ --out_system_ext $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM_EXT) \ --out_vendor $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) \ --out_odm $(PRODUCT_OUT)/$(TARGET_COPY_OUT_ODM) \ --out_product $(PRODUCT_OUT)/$(TARGET_COPY_OUT_PRODUCT) \ > $@ $(call dist-for-goals,droidcore-unbundled,$(host_init_verifier_output)) ================================================ FILE: core/tasks/mcts.mk ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifneq ($(wildcard test/mts/README.md),) mcts_test_suites := mcts_all_test_suites := mcts_all_test_suites += mcts $(foreach module, $(mts_modules), $(eval mcts_test_suites += mcts-$(module))) $(foreach suite, $(mcts_test_suites), \ $(eval test_suite_name := $(suite)) \ $(eval test_suite_tradefed := mts-tradefed) \ $(eval test_suite_readme := test/mts/README.md) \ $(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \ $(eval .PHONY: $(suite)) \ $(eval $(suite): $(compatibility_zip)) \ $(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \ ) $(foreach suite, $(mcts_all_test_suites), \ $(eval test_suite_name := $(suite)) \ $(eval test_suite_tradefed := mcts-tradefed) \ $(eval test_suite_readme := test/mts/README.md) \ $(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \ $(eval .PHONY: $(suite)) \ $(eval $(suite): $(compatibility_zip)) \ $(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \ ) endif ================================================ FILE: core/tasks/meta-lic.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Declare license metadata for non-module files released with products. # Moved here from device/generic/car/Android.mk $(eval $(call declare-1p-copy-files,device/generic/car,)) # Moved here from device/generic/trusty/Android.mk $(eval $(call declare-1p-copy-files,device/generic/trusty,)) # Moved here from device/generic/uml/Android.mk $(eval $(call declare-1p-copy-files,device/generic/uml,)) # Moved here from device/google_car/common/Android.mk $(eval $(call declare-1p-copy-files,device/google_car/common,)) # Moved here from device/google/atv/Android.mk $(eval $(call declare-1p-copy-files,device/google/atv,atv-component-overrides.xml)) $(eval $(call declare-1p-copy-files,device/google/atv,tv_core_hardware.xml)) # Moved here from device/google/cuttlefish/Android.mk $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,.idc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.cutf_cvm.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,audio_policy.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish/shared/config,pci.ids,SPDX-license-identifier-BSD-3-Clause,notice,device/google/cuttlefish/shared/config/LICENSE_BSD,)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,privapp-permissions-cuttlefish.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_profiles_V1_0.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_performance.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,cuttlefish_excluded_hardware.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_google_video.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,car_audio_configuration.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,audio_policy_configuration.xml)) $(eval $(call declare-1p-copy-files,device/google/cuttlefish,preinstalled-packages-product-car-cuttlefish.xml)) $(eval $(call declare-1p-copy-files,hardware/google/camera/devices,.json)) # Moved here from device/google/gs101/Android.mk $(eval $(call declare-copy-files-license-metadata,device/google/gs101,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,p2p_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-1p-copy-files,device/google/gs101,audio_policy_configuration.xml)) # Move here from device/google/raviole/Android.mk $(eval $(call declare-copy-files-license-metadata,device/google/raviole,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci-raven.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,)) $(eval $(call declare-1p-copy-files,device/google/raviole,audio_policy_configuration.xml)) # Moved here from device/sample/Android.mk $(eval $(call declare-1p-copy-files,device/sample,)) # Moved here from device/google/trout/Android.mk $(eval $(call declare-1p-copy-files,device/google/trout,)) # Moved here from frameworks/av/media/Android.mk $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.conf)) $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.xml)) $(eval $(call declare-1p-copy-files,frameworks/av/media/libstagefright,)) # Moved here from frameworks/av/services/Android.mk $(eval $(call declare-1p-copy-files,frameworks/av/services/audiopolicy,)) # Moved here from frameworks/base/Android.mk $(eval $(call declare-1p-copy-files,frameworks/base,.ogg)) $(eval $(call declare-1p-copy-files,frameworks/base,.kl)) $(eval $(call declare-1p-copy-files,frameworks/base,.kcm)) $(eval $(call declare-1p-copy-files,frameworks/base,.idc)) $(eval $(call declare-1p-copy-files,frameworks/base,dirty-image-objects)) $(eval $(call declare-1p-copy-files,frameworks/base/config,)) $(eval $(call declare-1p-copy-files,frameworks/native/data,)) # Moved here from hardware/google/camera/Android.mk $(eval $(call declare-1p-copy-files,hardware/google/camera,)) # Moved here from hardware/interfaces/tv/Android.mk $(eval $(call declare-1p-copy-files,hardware/interfaces/tv,tuner_vts_config_1_0.xml)) $(eval $(call declare-1p-copy-files,hardware/interfaces/tv,tuner_vts_config_1_1.xml)) # Moved here from device/generic/goldfish/Android.mk $(eval $(call declare-1p-copy-files,device/generic/goldfish/data,)) $(eval $(call declare-1p-copy-files,device/generic/goldfish/input,)) $(eval $(call declare-1p-copy-files,device/generic/goldfish/wifi,)) $(eval $(call declare-1p-copy-files,device/generic/goldfish/camera,)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,hals.conf)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,init.qemu-adb-keys.sh)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,init.system_ext.rc)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,.json)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,ueventd.rc)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,wpa_supplicant.conf)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,media_profiles_V1_0.xml)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu.rc)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,fstab.ranchu)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,display_settings.xml)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,display_settings_freeform.xml)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,device_state_configuration.xml)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu-core.sh)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,init.ranchu-net.sh)) $(eval $(call declare-1p-copy-files,device/generic/goldfish,audio_policy_configuration.xml)) # Moved here from packages/services/Car/Android.mk $(eval $(call declare-1p-copy-files,packages/services/Car,)) # Moved here from hardware/libhardware_legacy/Android.mk $(eval $(call declare-1p-copy-files,hardware/libhardware_legacy,)) # Moved here from system/core/rootdir/Android.mk $(eval $(call declare-1p-copy-files,system/core/rootdir,)) ================================================ FILE: core/tasks/module-info.mk ================================================ # Print a list of the modules that could be built # Currently runtime_dependencies only include the runtime libs information for cc binaries. MODULE_INFO_JSON := $(PRODUCT_OUT)/module-info.json COMMA := , _NEWLINE := '\n' define write-optional-json-list $(if $(strip $(2)),'$(COMMA)$(strip $(1)): [$(KATI_foreach_sep w,$(COMMA) ,$(2),"$(w)")]') endef define write-optional-json-bool $(if $(strip $(2)),'$(COMMA)$(strip $(1)): "$(strip $(2))"') endef SOONG_MODULE_INFO := $(SOONG_OUT_DIR)/module-info-$(TARGET_PRODUCT)${COVERAGE_SUFFIX}.json $(MODULE_INFO_JSON): PRIVATE_SOONG_MODULE_INFO := $(SOONG_MODULE_INFO) $(MODULE_INFO_JSON): PRIVATE_MERGE_JSON_OBJECTS := $(HOST_OUT_EXECUTABLES)/merge_module_info_json $(MODULE_INFO_JSON): $(HOST_OUT_EXECUTABLES)/merge_module_info_json $(MODULE_INFO_JSON): $(SOONG_MODULE_INFO) @echo Generating $@ $(hide) echo -ne '{\n ' > $@.tmp $(hide) echo -ne $(KATI_foreach_sep m,$(COMMA)$(_NEWLINE), $(sort $(ALL_MAKE_MODULE_INFO_JSON_MODULES)),\ '"$(m)": {' \ '"module_name": "$(ALL_MODULES.$(m).MODULE_NAME)"' \ $(call write-optional-json-list, "class", $(sort $(ALL_MODULES.$(m).CLASS))) \ $(call write-optional-json-list, "path", $(sort $(ALL_MODULES.$(m).PATH))) \ $(call write-optional-json-list, "tags", $(sort $(ALL_MODULES.$(m).TAGS))) \ $(call write-optional-json-list, "installed", $(sort $(ALL_MODULES.$(m).INSTALLED))) \ $(call write-optional-json-list, "compatibility_suites", $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))) \ $(call write-optional-json-list, "auto_test_config", $(sort $(ALL_MODULES.$(m).auto_test_config))) \ $(call write-optional-json-list, "test_config", $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS))) \ $(call write-optional-json-list, "dependencies", $(sort $(ALL_MODULES.$(m).ALL_DEPS))) \ $(call write-optional-json-list, "required", $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))) \ $(call write-optional-json-list, "shared_libs", $(sort $(ALL_MODULES.$(m).SHARED_LIBS))) \ $(call write-optional-json-list, "static_libs", $(sort $(ALL_MODULES.$(m).STATIC_LIBS))) \ $(call write-optional-json-list, "system_shared_libs", $(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS))) \ $(call write-optional-json-list, "srcs", $(sort $(ALL_MODULES.$(m).SRCS))) \ $(call write-optional-json-list, "srcjars", $(sort $(ALL_MODULES.$(m).SRCJARS))) \ $(call write-optional-json-list, "classes_jar", $(sort $(ALL_MODULES.$(m).CLASSES_JAR))) \ $(call write-optional-json-list, "test_mainline_modules", $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))) \ $(call write-optional-json-bool, "is_unit_test", $(ALL_MODULES.$(m).IS_UNIT_TEST)) \ $(call write-optional-json-list, "test_options_tags", $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))) \ $(call write-optional-json-list, "data", $(sort $(ALL_MODULES.$(m).TEST_DATA))) \ $(call write-optional-json-list, "runtime_dependencies", $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))) \ $(call write-optional-json-list, "static_dependencies", $(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES))) \ $(call write-optional-json-list, "data_dependencies", $(sort $(ALL_MODULES.$(m).TEST_DATA_BINS))) \ $(call write-optional-json-list, "supported_variants", $(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS))) \ $(call write-optional-json-list, "host_dependencies", $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))) \ $(call write-optional-json-list, "target_dependencies", $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))) \ $(call write-optional-json-bool, "test_module_config_base", $(ALL_MODULES.$(m).TEST_MODULE_CONFIG_BASE)) \ $(call write-optional-json-bool, "make", $(if $(ALL_MODULES.$(m).IS_SOONG_MODULE),,true)) \ $(call write-optional-json-bool, "make_generated_module_info", true) \ '}')'\n}\n' >> $@.tmp $(PRIVATE_MERGE_JSON_OBJECTS) -o $@ $(PRIVATE_SOONG_MODULE_INFO) $@.tmp rm $@.tmp .PHONY: module-info module-info: $(MODULE_INFO_JSON) droidcore-unbundled: $(MODULE_INFO_JSON) $(call dist-for-goals, general-tests, $(MODULE_INFO_JSON)) $(call dist-for-goals, droidcore-unbundled, $(MODULE_INFO_JSON)) # On every build, generate an all_modules.txt file to be used for autocompleting # the m command. After timing this using $(shell date +"%s.%3N"), it only adds # 0.01 seconds to the internal master build, and will only rerun on builds that # rerun kati. $(file >$(PRODUCT_OUT)/all_modules.txt,$(subst $(space),$(newline),$(ALL_MODULES))) ================================================ FILE: core/tasks/mts.mk ================================================ # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifneq ($(wildcard test/mts/README.md),) mts_test_suites := mts_test_suites += mts $(foreach module, $(mts_modules), $(eval mts_test_suites += mts-$(module))) $(foreach suite, $(mts_test_suites), \ $(eval test_suite_name := $(suite)) \ $(eval test_suite_tradefed := mts-tradefed) \ $(eval test_suite_readme := test/mts/README.md) \ $(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \ $(eval .PHONY: $(suite)) \ $(eval $(suite): $(compatibility_zip)) \ $(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \ ) endif ================================================ FILE: core/tasks/multitree.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: update-meta update-meta: $(SOONG_MULTITREE_METADATA) ================================================ FILE: core/tasks/oem_image.mk ================================================ # # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # We build oem.img only if it's asked for. ifneq ($(filter $(MAKECMDGOALS),oem_image),) ifndef BOARD_OEMIMAGE_PARTITION_SIZE $(error BOARD_OEMIMAGE_PARTITION_SIZE is not set.) endif INTERNAL_OEMIMAGE_FILES := \ $(filter $(TARGET_OUT_OEM)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) oemimage_intermediates := \ $(call intermediates-dir-for,PACKAGING,oem) BUILT_OEMIMAGE_TARGET := $(PRODUCT_OUT)/oem.img # We just build this directly to the install location. INSTALLED_OEMIMAGE_TARGET := $(BUILT_OEMIMAGE_TARGET) $(INSTALLED_OEMIMAGE_TARGET) : $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_OEMIMAGE_FILES) $(call pretty,"Target oem fs image: $@") @mkdir -p $(TARGET_OUT_OEM) @mkdir -p $(oemimage_intermediates) && rm -rf $(oemimage_intermediates)/oem_image_info.txt $(call generate-image-prop-dictionary, $(oemimage_intermediates)/oem_image_info.txt,oem,skip_fsck=true) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(TARGET_OUT_OEM) $(oemimage_intermediates)/oem_image_info.txt $@ $(TARGET_OUT) $(call assert-max-image-size,$@,$(BOARD_OEMIMAGE_PARTITION_SIZE)) .PHONY: oem_image oem_image : $(INSTALLED_OEMIMAGE_TARGET) $(call dist-for-goals, oem_image, $(INSTALLED_OEMIMAGE_TARGET)) $(call declare-1p-container,$(INSTALLED_OEMIMAGE_TARGET),) $(call declare-container-license-deps,$(INSTALLED_OEMIMAGE_TARGET),$(INTERNAL_USERIMAGE_DEPS) $(INTERNAL_OEMIMAGE_FILES),$(INSTALLED_OEMIMAGE_TARGET):) endif # oem_image in $(MAKECMDGOALS) ================================================ FILE: core/tasks/offline-sdk-docs.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip. # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to # $(OUT_DOCS)/offline-sdk. $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip $(hide) rm -rf $(OUT_DOCS)/offline-sdk $(hide) mkdir -p $(OUT_DOCS)/offline-sdk ( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1 .PHONY: docs offline-sdk-docs docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp ================================================ FILE: core/tasks/owners.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Create an artifact to include OWNERS files in source tree. .PHONY: owners intermediates := $(call intermediates-dir-for,PACKAGING,owners) owners_zip := $(intermediates)/owners.zip owners_list := $(OUT_DIR)/.module_paths/OWNERS.list owners := $(file <$(owners_list)) $(owners_zip) : PRIVATE_owners := $(subst $(newline),\n,$(owners)) $(owners_zip) : $(owners) $(SOONG_ZIP) @echo "Building artifact to include OWNERS files." rm -rf $@ echo -e "$(PRIVATE_owners)" > $@.list $(SOONG_ZIP) -o $@ -C . -l $@.list rm -f $@.list owners : $(owners_zip) $(call dist-for-goals, general-tests, $(owners_zip)) $(call declare-0p-target,$(owners_zip)) ================================================ FILE: core/tasks/performance-tests.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: performance-tests performance_tests_zip := $(PRODUCT_OUT)/performance-tests.zip # Create an artifact to include a list of test config files in performance-tests. performance_tests_list_zip := $(PRODUCT_OUT)/performance-tests_list.zip # Create an artifact to include all test config files in performance-tests. performance_tests_configs_zip := $(PRODUCT_OUT)/performance-tests_configs.zip $(performance_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(performance_tests_list_zip) $(performance_tests_configs_zip) $(performance_tests_zip) : PRIVATE_performance_tests_list_zip := $(performance_tests_list_zip) $(performance_tests_zip) : PRIVATE_performance_tests_configs_zip := $(performance_tests_configs_zip) $(performance_tests_zip) : PRIVATE_performance_tests_list := $(PRODUCT_OUT)/performance-tests_list $(performance_tests_zip) : $(COMPATIBILITY.performance-tests.FILES) $(SOONG_ZIP) echo $(sort $(COMPATIBILITY.performance-tests.FILES)) | tr " " "\n" > $@.list grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true $(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list -sha256 $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_performance_tests_configs_zip) \ -P host -C $(HOST_OUT) -l $@-host-test-configs.list \ -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list rm -f $(PRIVATE_performance_tests_list) $(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_performance_tests_list) $(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_performance_tests_list) $(hide) $(SOONG_ZIP) -d -o $(PRIVATE_performance_tests_list_zip) -C $(dir $@) -f $(PRIVATE_performance_tests_list) rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \ $(PRIVATE_performance_tests_list) performance-tests: $(performance_tests_zip) $(call dist-for-goals, performance-tests, $(performance_tests_zip) $(performance_tests_list_zip) $(performance_tests_configs_zip)) $(call declare-1p-container,$(performance_tests_zip),) $(call declare-container-license-deps,$(performance_tests_zip),$(COMPATIBILITY.performance-tests.FILES),$(PRODUCT_OUT)/:/) tests: performance-tests # Reset temp vars performance_tests_zip := performance_tests_list_zip := performance_tests_configs_zip := ================================================ FILE: core/tasks/platform_availability_check.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Check whether there is any module that isn't available for platform # is installed to the platform. # Skip for unbundled builds that don't produce a platform image. ifeq (,$(TARGET_BUILD_UNBUNDLED)) # Filter FAKE and NON_INSTALLABLE modules out and then collect those are not # available for platform _modules_not_available_for_platform := \ $(strip $(foreach m,$(product_MODULES),\ $(if $(filter-out FAKE,$(ALL_MODULES.$(m).CLASS)),\ $(if $(ALL_MODULES.$(m).INSTALLED),\ $(if $(filter true,$(ALL_MODULES.$(m).NOT_AVAILABLE_FOR_PLATFORM)),\ $(m)))))) ifndef ALLOW_MISSING_DEPENDENCIES _violators_with_path := $(foreach m,$(sort $(_modules_not_available_for_platform)),\ $(m):$(word 1,$(ALL_MODULES.$(m).PATH))\ ) $(call maybe-print-list-and-error,$(_violators_with_path),\ Following modules are requested to be installed. But are not available \ for platform because they do not have "//apex_available:platform" or \ they depend on other modules that are not available for platform) else # Don't error out immediately when ALLOW_MISSING_DEPENDENCIES is set. # Instead, add a dependency on a rule that prints the error message. define not_available_for_platform_rule not_installable_file := $(patsubst $(OUT_DIR)/%,$(OUT_DIR)/NOT_AVAILABLE_FOR_PLATFORM/%,$(1)) $(1): $$(not_installable_file) $$(not_installable_file): $(call echo-error,$(2),Module is requested to be installed but is not \ available for platform because it does not have "//apex_available:platform" or \ it depends on other modules that are not available for platform.) exit 1 endef $(foreach m,$(_modules_not_available_for_platform),\ $(foreach i,$(filter-out $(HOST_OUT)/%,$(ALL_MODULES.$(m).INSTALLED)),\ $(eval $(call not_available_for_platform_rule,$(i),$(m))))) endif endif ================================================ FILE: core/tasks/prebuilt_tradefed.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifeq (,$(wildcard tools/tradefederation/core)) .PHONY: tradefed-core tradefed-core: tradefed atest_tradefed.sh .PHONY: tradefed-all tradefed-all: tradefed atest_tradefed.sh $(call dist-for-goals, tradefed, $(HOST_OUT)/etc/tradefed.zip) endif ================================================ FILE: core/tasks/sdk-addon.mk ================================================ # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: sdk_addon # If they didn't define PRODUCT_SDK_ADDON_NAME, then we won't define # any of these rules. addon_name := $(PRODUCT_SDK_ADDON_NAME) ifneq ($(addon_name),) addon_dir_leaf := $(addon_name)-$(INTERNAL_SDK_HOST_OS_NAME) addon_dir_img := $(addon_dir_leaf)-img intermediates := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates full_target := $(HOST_OUT_SDK_ADDON)/$(addon_dir_leaf).zip full_target_dist_name := $(addon_name)-FILE_NAME_TAG_PLACEHOLDER-$(INTERNAL_SDK_HOST_OS_NAME) full_target_img := $(HOST_OUT_SDK_ADDON)/$(addon_dir_img).zip staging := $(intermediates) sdk_addon_deps := files_to_copy := define stub-addon-jar-file $(subst .jar,_stub-addon.jar,$(1)) endef define stub-addon-jar $(call stub-addon-jar-file,$(1)): $(1) | mkstubs $(info Stubbing addon jar using $(PRODUCT_SDK_ADDON_STUB_DEFS)) $(hide) $(JAVA) -jar $(call module-installed-files,mkstubs) $(if $(hide),,--v) \ "$$<" "$$@" @$(PRODUCT_SDK_ADDON_STUB_DEFS) endef # Files that are built and then copied into the sdk-addon ifneq ($(PRODUCT_SDK_ADDON_COPY_MODULES),) $(foreach cf,$(PRODUCT_SDK_ADDON_COPY_MODULES), \ $(eval _src := $(call module-stubs-files,$(call word-colon,1,$(cf)))) \ $(eval $(call stub-addon-jar,$(_src))) \ $(eval _src := $(call stub-addon-jar-file,$(_src))) \ $(if $(_src),,$(eval $(error Unknown or unlinkable module: $(call word-colon,1,$(cf)). Requested by $(INTERNAL_PRODUCT)))) \ $(eval _dest := $(call word-colon,2,$(cf))) \ $(eval files_to_copy += $(addon_dir_leaf):$(_src):$(_dest)) \ ) endif # Files that are copied directly into the sdk-addon ifneq ($(PRODUCT_SDK_ADDON_COPY_FILES),) $(foreach cf,$(PRODUCT_SDK_ADDON_COPY_FILES), \ $(eval _src := $(call word-colon,1,$(cf))) \ $(eval _dest := $(call word-colon,2,$(cf))) \ $(if $(findstring images/,$(_dest)), $(eval _root := $(addon_dir_img)), $(eval _root := $(addon_dir_leaf))) \ $(eval files_to_copy += $(_root):$(_src):$(_dest)) \ ) endif # Files copied in the system-image directory files_to_copy += \ $(addon_dir_img):$(INSTALLED_QEMU_SYSTEMIMAGE):images/$(TARGET_CPU_ABI)/system.img \ $(addon_dir_img):$(INSTALLED_QEMU_VENDORIMAGE):images/$(TARGET_CPU_ABI)/vendor.img \ $(addon_dir_img):$(INSTALLED_QEMU_RAMDISKIMAGE):images/$(TARGET_CPU_ABI)/ramdisk.img \ $(addon_dir_img):$(PRODUCT_OUT)/system/build.prop:images/$(TARGET_CPU_ABI)/build.prop \ $(addon_dir_img):device/generic/goldfish/data/etc/userdata.img:images/$(TARGET_CPU_ABI)/userdata.img \ $(addon_dir_img):$(target_notice_file_txt):images/$(TARGET_CPU_ABI)/NOTICE.txt \ $(addon_dir_img):$(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP):images/source.properties ifeq ($(BOARD_AVB_ENABLE),true) files_to_copy += \ $(addon_dir_img):$(QEMU_VERIFIED_BOOT_PARAMS):images/$(TARGET_CPU_ABI)/VerifiedBootParams.textproto endif # Generate rules to copy the requested files $(foreach cf,$(files_to_copy), \ $(eval _root := $(call word-colon,1,$(cf))) \ $(eval _src := $(call word-colon,2,$(cf))) \ $(eval _dest := $(call append-path,$(call append-path,$(staging),$(_root)),$(call word-colon,3,$(cf)))) \ $(eval $(call copy-one-file,$(_src),$(_dest))) \ $(eval sdk_addon_deps += $(_dest)) \ ) # The system-image source.properties is a template that we directly expand in-place addon_img_source_prop := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI)/source.properties sdk_addon_deps += $(addon_img_source_prop) $(addon_img_source_prop): $(PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP) @echo Generate $@ $(hide) mkdir -p $(dir $@) $(hide) sed \ -e 's/$${PLATFORM_VERSION}/$(PLATFORM_VERSION)/' \ -e 's/$${PLATFORM_SDK_VERSION}/$(PLATFORM_SDK_VERSION)/' \ -e 's/$${PLATFORM_VERSION_CODENAME}/$(subst REL,,$(PLATFORM_VERSION_CODENAME))/' \ -e 's/$${TARGET_ARCH}/$(TARGET_ARCH)/' \ -e 's/$${TARGET_CPU_ABI}/$(TARGET_CPU_ABI)/' \ $< > $@ && sed -i -e '/^AndroidVersion.CodeName=\s*$$/d' $@ # We don't know about all of the docs files, so depend on the timestamps for # them, and record the directories, and the packaging rule will just copy the # whole thing. doc_modules := $(PRODUCT_SDK_ADDON_DOC_MODULES) sdk_addon_deps += $(foreach dm, $(doc_modules), $(call doc-timestamp-for, $(dm))) $(full_target): PRIVATE_DOCS_DIRS := $(addprefix $(OUT_DOCS)/, $(doc_modules)) $(full_target): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_leaf)) $(full_target): $(sdk_addon_deps) | $(SOONG_ZIP) @echo Packaging SDK Addon: $@ $(hide) mkdir -p $(PRIVATE_STAGING_DIR)/docs $(hide) for d in $(PRIVATE_DOCS_DIRS); do \ cp -R $$d $(PRIVATE_STAGING_DIR)/docs ;\ done $(hide) mkdir -p $(dir $@) $(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR) $(full_target_img): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI) $(full_target_img): $(full_target) $(addon_img_source_prop) | $(SOONG_ZIP) @echo Packaging SDK Addon System-Image: $@ $(hide) mkdir -p $(dir $@) cp -R $(PRODUCT_OUT)/data $(PRIVATE_STAGING_DIR) $(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR) sdk_addon: $(full_target) $(full_target_img) ifneq ($(sdk_repo_goal),) # If we're building the sdk_repo, keep the name of the addon zip # around so that development/build/tools/sdk_repo.mk can dist it # at the appropriate location. ADDON_SDK_ZIP := $(full_target) ADDON_SDK_IMG_ZIP := $(full_target_img) else # When not building an sdk_repo, just dist the addon zip file # as-is. $(call dist-for-goals, sdk_addon, $(full_target):$(full_target_dist_name)) endif else # addon_name ifneq ($(filter sdk_addon,$(MAKECMDGOALS)),) $(error Trying to build sdk_addon, but product '$(INTERNAL_PRODUCT)' does not define one) endif endif # addon_name ================================================ FILE: core/tasks/sts.mk ================================================ # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifneq ($(wildcard test/sts/README.md),) test_suite_name := sts test_suite_tradefed := sts-tradefed test_suite_readme := test/sts/README.md include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: sts sts: $(compatibility_zip) $(call dist-for-goals, sts, $(compatibility_zip)) endif ================================================ FILE: core/tasks/tools/build_custom_image.mk ================================================ # # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Define rule to build one custom image. # Input variables: my_custom_imag_makefile $(call clear-var-list, $(custom_image_parameter_variables)) include $(my_custom_imag_makefile) my_custom_image_name := $(basename $(notdir $(my_custom_imag_makefile))) intermediates := $(call intermediates-dir-for,PACKAGING,$(my_custom_image_name)) my_built_custom_image := $(intermediates)/$(my_custom_image_name).img my_staging_dir := $(intermediates)/$(CUSTOM_IMAGE_MOUNT_POINT) # Collect CUSTOM_IMAGE_MODULES's installd files and their PICKUP_FILES. my_built_modules := my_copy_pairs := my_pickup_files := $(foreach m,$(CUSTOM_IMAGE_MODULES),\ $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\ $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\ $(if $(_pickup_files)$(_built_files),,\ $(warning Unknown installed file for module '$(m)'))\ $(eval my_pickup_files += $(_pickup_files))\ $(foreach i, $(_built_files),\ $(eval bui_ins := $(subst :,$(space),$(i)))\ $(eval ins := $(word 2,$(bui_ins)))\ $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\ $(eval bui := $(word 1,$(bui_ins)))\ $(eval my_built_modules += $(bui))\ $(eval my_copy_dest := $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))\ $(eval my_copy_dest := $(subst /,$(space),$(my_copy_dest)))\ $(eval my_copy_dest := $(wordlist 2,999,$(my_copy_dest)))\ $(eval my_copy_dest := $(subst $(space),/,$(my_copy_dest)))\ $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\ )) my_kernel_module_copy_files := my_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES ifdef $(my_custom_image_modules_var) $(foreach kmod,\ $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped)),\ $(eval pair := $(subst :,$(space),$(kmod)))\ $(eval my_kernel_module_copy_files += $(word 1,$(pair)):$(subst $(my_staging_dir)/,,$(word 2,$(pair))))) endif # Collect CUSTOM_IMAGE_COPY_FILES. my_image_copy_files := $(foreach f,$(CUSTOM_IMAGE_COPY_FILES) $(my_kernel_module_copy_files),\ $(eval pair := $(subst :,$(space),$(f)))\ $(eval src := $(word 1,$(pair)))\ $(eval my_image_copy_files += $(src))\ $(eval my_copy_pairs += $(src):$(my_staging_dir)/$(word 2,$(pair)))) ifdef CUSTOM_IMAGE_AVB_KEY_PATH ifndef CUSTOM_IMAGE_AVB_ALGORITHM $(error CUSTOM_IMAGE_AVB_ALGORITHM is not defined) endif ifndef CUSTOM_IMAGE_AVB_ROLLBACK_INDEX $(error CUSTOM_IMAGE_AVB_ROLLBACK_INDEX is not defined) endif # set rollback_index via footer args CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX) CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(CUSTOM_IMAGE_AVB_ROLLBACK_INDEX) endif $(my_built_custom_image): PRIVATE_INTERMEDIATES := $(intermediates) $(my_built_custom_image): PRIVATE_MOUNT_POINT := $(CUSTOM_IMAGE_MOUNT_POINT) $(my_built_custom_image): PRIVATE_PARTITION_SIZE := $(CUSTOM_IMAGE_PARTITION_SIZE) $(my_built_custom_image): PRIVATE_FILE_SYSTEM_TYPE := $(CUSTOM_IMAGE_FILE_SYSTEM_TYPE) $(my_built_custom_image): PRIVATE_STAGING_DIR := $(my_staging_dir) $(my_built_custom_image): PRIVATE_COPY_PAIRS := $(my_copy_pairs) $(my_built_custom_image): PRIVATE_PICKUP_FILES := $(my_pickup_files) $(my_built_custom_image): PRIVATE_SELINUX := $(CUSTOM_IMAGE_SELINUX) $(my_built_custom_image): PRIVATE_VERITY_BLOCK_DEVICE := $(CUSTOM_IMAGE_VERITY_BLOCK_DEVICE) $(my_built_custom_image): PRIVATE_DICT_FILE := $(CUSTOM_IMAGE_DICT_FILE) $(my_built_custom_image): PRIVATE_AVB_AVBTOOL := $(AVBTOOL) $(my_built_custom_image): PRIVATE_AVB_KEY_PATH := $(CUSTOM_IMAGE_AVB_KEY_PATH) $(my_built_custom_image): PRIVATE_AVB_ALGORITHM:= $(CUSTOM_IMAGE_AVB_ALGORITHM) $(my_built_custom_image): PRIVATE_AVB_HASH_ENABLE := $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(my_built_custom_image): PRIVATE_AVB_ADD_HASH_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS) $(my_built_custom_image): PRIVATE_AVB_HASHTREE_ENABLE := $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE) $(my_built_custom_image): PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS := $(CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS) ifeq (true,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE))) $(my_built_custom_image): $(AVBTOOL) else ifneq (,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE))) $(error Cannot set both CUSTOM_IMAGE_AVB_HASH_ENABLE and CUSTOM_IMAGE_AVB_HASHTREE_ENABLE to true) endif ifeq ($(strip $(HAS_BUILD_NUMBER)),true) $(my_built_custom_image): $(BUILD_NUMBER_FILE) endif $(my_built_custom_image): $(INTERNAL_USERIMAGES_DEPS) $(my_built_modules) $(my_image_copy_files) $(my_custom_image_modules_dep) \ $(CUSTOM_IMAGE_DICT_FILE) @echo "Build image $@" $(hide) rm -rf $(PRIVATE_INTERMEDIATES) && mkdir -p $(PRIVATE_INTERMEDIATES) $(hide) rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR) # Copy all the files. $(hide) $(foreach p,$(PRIVATE_COPY_PAIRS),\ $(eval pair := $(subst :,$(space),$(p)))\ mkdir -p $(dir $(word 2,$(pair)));\ cp -Rf $(word 1,$(pair)) $(word 2,$(pair));) $(if $($(PRIVATE_PICKUP_FILES)),$(hide) cp -Rf $(PRIVATE_PICKUP_FILES) $(PRIVATE_STAGING_DIR)) # Generate the dict. $(hide) echo "# For all accepted properties, see BuildImage() in tools/releasetools/build_image.py" > $(PRIVATE_INTERMEDIATES)/image_info.txt $(hide) echo "mount_point=$(PRIVATE_MOUNT_POINT)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(hide) echo "partition_name=$(PRIVATE_MOUNT_POINT)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(hide) echo "fs_type=$(PRIVATE_FILE_SYSTEM_TYPE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(hide) echo "partition_size=$(PRIVATE_PARTITION_SIZE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(if $(PRIVATE_SELINUX),$(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) $(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo "verity_disable=true" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) $(hide) echo "avb_avbtool=$(PRIVATE_AVB_AVBTOOL)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt $(if $(PRIVATE_AVB_KEY_PATH),\ $(hide) echo "avb_key_path=$(PRIVATE_AVB_KEY_PATH)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ echo "avb_algorithm=$(PRIVATE_AVB_ALGORITHM)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) $(if $(PRIVATE_AVB_HASH_ENABLE),\ $(hide) echo "avb_hash_enable=$(PRIVATE_AVB_HASH_ENABLE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ echo "avb_add_hash_footer_args=$(PRIVATE_AVB_ADD_HASH_FOOTER_ARGS)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) $(if $(PRIVATE_AVB_HASHTREE_ENABLE),\ $(hide) echo "avb_hashtree_enable=$(PRIVATE_AVB_HASHTREE_ENABLE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ echo "avb_add_hashtree_footer_args=$(PRIVATE_AVB_ADD_HASHTREE_FOOTER_ARGS)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt) $(if $(PRIVATE_DICT_FILE),\ $(hide) echo "# Properties from $(PRIVATE_DICT_FILE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\ cat $(PRIVATE_DICT_FILE) >> $(PRIVATE_INTERMEDIATES)/image_info.txt) # Generate the image. $(if $(filter oem,$(PRIVATE_MOUNT_POINT)), \ $(hide) echo "oem.buildnumber=$(BUILD_NUMBER_FROM_FILE)" >> $(PRIVATE_STAGING_DIR)/oem.prop) $(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \ $(BUILD_IMAGE) \ $(PRIVATE_STAGING_DIR) $(PRIVATE_INTERMEDIATES)/image_info.txt $@ $(TARGET_OUT) my_installed_custom_image := $(PRODUCT_OUT)/$(notdir $(my_built_custom_image)) $(my_installed_custom_image) : $(my_built_custom_image) $(call copy-file-to-new-target-with-cp) .PHONY: $(my_custom_image_name) custom_images $(my_custom_image_name) : $(my_installed_custom_image) # Archive the built image. $(call dist-for-goals, $(my_custom_image_name) custom_images,$(my_installed_custom_image)) my_staging_dir := my_built_modules := my_copy_dest := my_copy_pairs := my_pickup_files := ================================================ FILE: core/tasks/tools/compatibility.mk ================================================ # Copyright (C) 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Package up a compatibility test suite in a zip file. # # Input variables: # test_suite_name: the name of this test suite eg. cts # test_suite_tradefed: the name of this test suite's tradefed wrapper # test_suite_dynamic_config: the path to this test suite's dynamic configuration file # test_suite_readme: the path to a README file for this test suite # test_suite_prebuilt_tools: the set of prebuilt tools to be included directly # in the 'tools' subdirectory of the test suite. # test_suite_tools: the set of tools for this test suite # # Output variables: # compatibility_zip: the path to the output zip file. special_mts_test_suites := special_mts_test_suites += $(mts_modules) ifneq ($(filter $(special_mts_test_suites),$(patsubst mcts-%,%,$(test_suite_name))),) test_suite_subdir := android-mts else ifneq ($(filter $(special_mts_test_suites),$(patsubst mts-%,%,$(test_suite_name))),) test_suite_subdir := android-mts else test_suite_subdir := android-$(test_suite_name) endif out_dir := $(HOST_OUT)/$(test_suite_name)/$(test_suite_subdir) test_artifacts := $(COMPATIBILITY.$(test_suite_name).FILES) test_tools := $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \ $(HOST_OUT_JAVA_LIBRARIES)/loganalysis.jar \ $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \ $(HOST_OUT_JAVA_LIBRARIES)/compatibility-tradefed.jar \ $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed).jar \ $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed)-tests.jar \ $(HOST_OUT_EXECUTABLES)/$(test_suite_tradefed) \ $(HOST_OUT_EXECUTABLES)/test-utils-script \ $(test_suite_readme) $(foreach f,$(test_suite_readme),$(if $(strip $(ALL_TARGETS.$(f).META_LIC)),,$(eval ALL_TARGETS.$(f).META_LIC := $(module_license_metadata)))) test_tools += $(test_suite_tools) # The JDK to package into the test suite zip file. Always package the linux JDK. test_suite_jdk_dir := $(ANDROID_JAVA_HOME)/../linux-x86 ifndef test_suite_jdk_files # This file gets included many times, so make sure we only run the $(shell) once. # Otherwise it will slow down every build due to all copies of it being rerun when kati # checks the stamp file. test_suite_jdk_files :=$= $(shell find $(test_suite_jdk_dir) -type f | sort) endif test_suite_jdk := $(call intermediates-dir-for,PACKAGING,$(test_suite_name)_jdk,HOST)/jdk.zip $(test_suite_jdk): PRIVATE_JDK_DIR := $(test_suite_jdk_dir) $(test_suite_jdk): PRIVATE_SUBDIR := $(test_suite_subdir) $(test_suite_jdk): $(test_suite_jdk_files) $(test_suite_jdk): $(SOONG_ZIP) $(SOONG_ZIP) -o $@ -P $(PRIVATE_SUBDIR)/jdk -C $(PRIVATE_JDK_DIR) -D $(PRIVATE_JDK_DIR) -sha256 $(call declare-license-metadata,$(test_suite_jdk),SPDX-license-identifier-GPL-2.0-with-classpath-exception,permissive,\ $(test_suite_jdk_dir)/legal/java.base/LICENSE,JDK,prebuilts/jdk/$(notdir $(patsubst %/,%,$(dir $(test_suite_jdk_dir))))) # Copy license metadata $(call declare-copy-target-license-metadata,$(out_dir)/$(notdir $(test_suite_jdk)),$(test_suite_jdk)) $(foreach t,$(test_tools) $(test_suite_prebuilt_tools),\ $(eval _dst := $(out_dir)/tools/$(notdir $(t)))\ $(if $(strip $(ALL_TARGETS.$(t).META_LIC)),\ $(call declare-copy-target-license-metadata,$(_dst),$(t)),\ $(warning $(t) has no license metadata)\ )\ ) test_copied_tools := $(foreach t,$(test_tools) $(test_suite_prebuilt_tools), $(out_dir)/tools/$(notdir $(t))) $(out_dir)/$(notdir $(test_suite_jdk)) # Include host shared libraries host_shared_libs := $(call copy-many-files, $(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES)) $(if $(strip $(host_shared_libs)),\ $(foreach p,$(COMPATIBILITY.$(test_suite_name).HOST_SHARED_LIBRARY.FILES),\ $(eval _src := $(call word-colon,1,$(p)))\ $(eval _dst := $(call word-colon,2,$(p)))\ $(if $(strip $(ALL_TARGETS.$(_src).META_LIC)),\ $(call declare-copy-target-license-metadata,$(_dst),$(_src)),\ $(warning $(_src) has no license metadata for $(_dst))\ )\ )\ ) compatibility_zip_deps := \ $(test_artifacts) \ $(test_tools) \ $(test_suite_prebuilt_tools) \ $(test_suite_dynamic_config) \ $(test_suite_jdk) \ $(MERGE_ZIPS) \ $(SOONG_ZIP) \ $(host_shared_libs) \ $(test_suite_extra_deps) \ compatibility_zip_resources := $(out_dir)/tools $(out_dir)/testcases $(out_dir)/lib $(out_dir)/lib64 # Test Suite NOTICE files test_suite_notice_txt := $(out_dir)/NOTICE.txt test_suite_notice_html := $(out_dir)/NOTICE.html compatibility_zip_deps += $(test_suite_notice_txt) compatibility_zip_resources += $(test_suite_notice_txt) compatibility_tests_list_zip := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name)-tests_list.zip compatibility_zip := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name).zip $(compatibility_zip) : .KATI_IMPLICIT_OUTPUTS := $(compatibility_tests_list_zip) $(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir) $(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools) $(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name) $(compatibility_zip): PRIVATE_DYNAMIC_CONFIG := $(test_suite_dynamic_config) $(compatibility_zip): PRIVATE_RESOURCES := $(compatibility_zip_resources) $(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk) $(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list $(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip) ifeq ($(strip $(HAS_BUILD_NUMBER)),true) $(compatibility_zip): $(BUILD_NUMBER_FILE) endif $(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP) # Make dir structure mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases rm -f $@ $@.tmp $@.jdk echo $(BUILD_NUMBER_FROM_FILE) > $(PRIVATE_OUT_DIR)/tools/version.txt # Copy tools cp $(PRIVATE_TOOLS) $(PRIVATE_OUT_DIR)/tools $(if $(PRIVATE_DYNAMIC_CONFIG),$(hide) cp $(PRIVATE_DYNAMIC_CONFIG) $(PRIVATE_OUT_DIR)/testcases/$(PRIVATE_SUITE_NAME).dynamic) find $(PRIVATE_RESOURCES) | sort >$@.list $(SOONG_ZIP) -d -o $@.tmp -C $(dir $@) -l $@.list -sha256 $(MERGE_ZIPS) $@ $@.tmp $(PRIVATE_JDK) rm -f $@.tmp # Build a list of tests rm -f $(PRIVATE_tests_list) $(hide) grep -e .*\\.config$$ $@.list | sed s%$(PRIVATE_OUT_DIR)/testcases/%%g > $(PRIVATE_tests_list) $(SOONG_ZIP) -d -o $(PRIVATE_tests_list_zip) -j -f $(PRIVATE_tests_list) rm -f $(PRIVATE_tests_list) $(call declare-0p-target,$(compatibility_tests_list_zip),) $(call declare-1p-container,$(compatibility_zip),) $(call declare-container-license-deps,$(compatibility_zip),$(compatibility_zip_deps) $(test_copied_tools), $(out_dir)/:/) $(eval $(call html-notice-rule,$(test_suite_notice_html),"Test suites","Notices for files contained in the test suites filesystem image:",$(compatibility_zip),$(compatibility_zip))) $(eval $(call text-notice-rule,$(test_suite_notice_txt),"Test suites","Notices for files contained in the test suites filesystem image:",$(compatibility_zip),$(compatibility_zip))) $(call declare-0p-target,$(test_suite_notice_html)) $(call declare-0p-target,$(test_suite_notice_txt)) $(call declare-1p-copy-files,$(test_suite_dynamic_config),) $(call declare-1p-copy-files,$(test_suite_prebuilt_tools),) # Reset all input variables test_suite_name := test_suite_tradefed := test_suite_dynamic_config := test_suite_readme := test_suite_prebuilt_tools := test_suite_tools := test_suite_jdk := test_suite_jdk_dir := host_shared_libs := test_suite_extra_deps := ================================================ FILE: core/tasks/tools/package-modules.mk ================================================ # Package up modules to a zip file. # It preserves the install path of the modules' installed files. # # Input variables: # my_modules: a list of module names # my_package_name: the name of the output zip file. # my_copy_pairs: a list of extra files to install (in src:dest format) # Optional input variables: # my_modules_strict: what happens when a module from my_modules does not exist # "true": error out when a module is missing # "false": print a warning when a module is missing # "": defaults to false currently # Output variables: # my_package_zip: the path to the output zip file. # # my_makefile := $(lastword $(filter-out $(lastword $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) include $(CLEAR_VARS) LOCAL_MODULE := $(my_package_name) LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 LOCAL_LICENSE_CONDITIONS := notice LOCAL_LICENSE_PACKAGE_NAME := Android LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE LOCAL_MODULE_CLASS := PACKAGING LOCAL_MODULE_STEM := $(my_package_name).zip LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_SYSTEM)/base_rules.mk my_staging_dir := $(intermediates)/staging my_package_zip := $(LOCAL_BUILT_MODULE) my_built_modules := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p))) my_copy_pairs := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p)):$(my_staging_dir)/$(call word-colon,2,$(p))) my_pickup_files := my_missing_error := # Iterate over the modules and include their direct dependencies stated in the # LOCAL_REQUIRED_MODULES. my_modules_and_deps := $(my_modules) $(foreach m,$(my_modules),\ $(eval _explicitly_required := \ $(strip $(ALL_MODULES.$(m).EXPLICITLY_REQUIRED_FROM_TARGET)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).EXPLICITLY_REQUIRED_FROM_TARGET)))\ $(eval my_modules_and_deps += $(_explicitly_required))\ ) ifneq ($(filter-out true false,$(my_modules_strict)),) $(shell $(call echo-error,$(my_makefile),$(my_package_name): Invalid value for 'my_module_strict' = '$(my_modules_strict)'. Valid values: 'true', 'false', '')) $(error done) endif my_missing_files = $(shell $(call echo-warning,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)'))$(shell$(call echo-warning,$(my_makefile),$(my_package_name): Some necessary modules may have been skipped by Soong. Check if PRODUCT_SOURCE_ROOT_DIRS is pruning necessary Android.bp files.)) ifeq ($(ALLOW_MISSING_DEPENDENCIES),true) # Ignore unknown installed files on partial builds my_missing_files = else ifneq ($(my_modules_strict),false) my_missing_files = $(shell $(call echo-error,$(my_makefile),$(my_package_name): Unknown installed file for module '$(1)'))$(shell$(call echo-warning,$(my_makefile),$(my_package_name): Some necessary modules may have been skipped by Soong. Check if PRODUCT_SOURCE_ROOT_DIRS is pruning necessary Android.bp files.))$(eval my_missing_error := true) endif # Iterate over modules' built files and installed files; # Calculate the dest files in the output zip file. $(foreach m,$(my_modules_and_deps),\ $(eval _pickup_files := $(strip $(ALL_MODULES.$(m).PICKUP_FILES)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\ $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\ $(eval _module_class_folder := $($(strip MODULE_CLASS_$(word 1, $(strip $(ALL_MODULES.$(m).CLASS)\ $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).CLASS))))))\ $(if $(_pickup_files)$(_built_files),,\ $(call my_missing_files,$(m)))\ $(eval my_pickup_files += $(_pickup_files))\ $(foreach i, $(_built_files),\ $(eval bui_ins := $(subst :,$(space),$(i)))\ $(eval ins := $(word 2,$(bui_ins)))\ $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\ $(eval bui := $(word 1,$(bui_ins)))\ $(eval my_built_modules += $(bui))\ $(if $(filter $(_module_class_folder), nativetest benchmarktest),\ $(eval module_class_folder_stem := $(_module_class_folder)$(findstring 64, $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))),\ $(eval module_class_folder_stem := $(_module_class_folder)))\ $(eval my_copy_dest := $(patsubst data/%,DATA/%,\ $(patsubst testcases/%,DATA/$(module_class_folder_stem)/%,\ $(patsubst testcases/$(m)/$(TARGET_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\ $(patsubst testcases/$(m)/$(TARGET_2ND_ARCH)/%,DATA/$(module_class_folder_stem)/$(m)/%,\ $(patsubst system/%,DATA/%,\ $(patsubst $(PRODUCT_OUT)/%,%,$(ins))))))))\ $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\ )) ifneq ($(my_missing_error),) $(error done) endif $(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs) $(my_package_zip): PRIVATE_STAGING_DIR := $(my_staging_dir) $(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files) $(my_package_zip) : $(my_built_modules) $(SOONG_ZIP) @echo "Package $@" @rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR) $(foreach p, $(PRIVATE_COPY_PAIRS),\ $(eval pair := $(subst :,$(space),$(p)))\ mkdir -p $(dir $(word 2,$(pair))) && \ cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true $(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\ cp -RfL $(f) $(PRIVATE_STAGING_DIR) && ) true $(hide) $(SOONG_ZIP) -o $@ -C $(PRIVATE_STAGING_DIR) -D $(PRIVATE_STAGING_DIR) rm -rf $(PRIVATE_STAGING_DIR) my_makefile := my_staging_dir := my_built_modules := my_copy_dest := my_copy_pairs := my_pickup_files := my_missing_files := my_missing_error := my_modules_and_deps := my_modules_strict := ================================================ FILE: core/tasks/tradefed-tests-list.mk ================================================ # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # List all TradeFed tests from COMPATIBILITY.tradefed_tests_dir .PHONY: tradefed-tests-list COMPATIBILITY.tradefed_tests_dir := \ $(COMPATIBILITY.tradefed_tests_dir) \ tools/tradefederation/core/res/config \ tools/tradefederation/core/javatests/res/config \ vendor/google_tradefederation/contrib/res/config \ vendor/google_tradefederation/core/res/config \ vendor/google_tradefederation/core/javatests/res/config \ vendor/google_tradefederation/core/prod_tests/res/config tradefed_tests := $(foreach dir, $(COMPATIBILITY.tradefed_tests_dir), \ $(if $(wildcard $(dir)/*), \ $(eval tradefed_tests += $(shell find $(dir) -type f -name "*.xml")) \ ) \ ) tradefed_tests_list_intermediates := $(call intermediates-dir-for,PACKAGING,tradefed_tests_list,HOST,COMMON) tradefed_tests_list_zip := $(tradefed_tests_list_intermediates)/tradefed-tests_list.zip all_tests := $(foreach test, $(tradefed_tests), \ $(eval all_tests += $(word 2,$(subst /res/config/,$(space),$(test))))) $(tradefed_tests_list_zip) : PRIVATE_tradefed_tests := $(subst .xml,,$(subst $(space),\n,$(sort $(all_tests)))) $(tradefed_tests_list_zip) : PRIVATE_tradefed_tests_list := $(tradefed_tests_list_intermediates)/tradefed-tests_list $(tradefed_tests_list_zip) : $(tradefed_tests) $(SOONG_ZIP) @echo "Package: $@" $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@) $(hide) echo -e "$(PRIVATE_tradefed_tests)" > $(PRIVATE_tradefed_tests_list) $(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@) -f $(PRIVATE_tradefed_tests_list) tradefed-tests-list : $(tradefed_tests_list_zip) $(call dist-for-goals, tradefed-tests-list, $(tradefed_tests_list_zip)) $(call declare-1p-target,$(tradefed_tests_list_zip),) tests: tradefed-tests-list ================================================ FILE: core/tasks/vendor_module_check.mk ================================================ # # Copyright (C) 2011 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Restrict the vendor module owners here. _vendor_owner_allowed_list := \ asus \ audience \ atmel \ broadcom \ csr \ elan \ fpc \ google \ htc \ huawei \ imgtec \ invensense \ intel \ lge \ moto \ mtk \ nvidia \ nxp \ nxpsw \ qcom \ qti \ samsung \ samsung_arm \ sony \ synaptics \ ti \ trusted_logic \ verizon \ waves \ widevine _restrictions := $(PRODUCT_RESTRICT_VENDOR_FILES) ifneq (,$(_restrictions)) ifneq (,$(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES)) $(error Error: cannot set both PRODUCT_RESTRICT_VENDOR_FILES and VENDOR_PRODUCT_RESTRICT_VENDOR_FILES) endif _vendor_exception_path_prefix := _vendor_exception_modules := else _restrictions := $(VENDOR_PRODUCT_RESTRICT_VENDOR_FILES) _vendor_exception_path_prefix := $(patsubst %, vendor/%/%, $(VENDOR_EXCEPTION_PATHS)) _vendor_exception_modules := $(VENDOR_EXCEPTION_MODULES) endif ifneq (,$(_restrictions)) _vendor_check_modules := \ $(foreach m, $(filter-out $(_vendor_exception_modules), $(product_MODULES)), \ $(if $(filter-out FAKE, $(ALL_MODULES.$(m).CLASS)),\ $(if $(filter vendor/%, $(ALL_MODULES.$(m).PATH)),\ $(if $(filter-out $(_vendor_exception_path_prefix), $(ALL_MODULES.$(m).PATH)),\ $(m))))) _vendor_module_owner_info := # Restrict owners ifneq (,$(filter true owner all, $(_restrictions))) _vendor_package_overlays := $(filter-out $(_vendor_exception_path_prefix),\ $(filter vendor/%, $(PRODUCT_PACKAGE_OVERLAYS) $(DEVICE_PACKAGE_OVERLAYS))) ifneq (,$(_vendor_package_overlays)) $(error Error: Product "$(TARGET_PRODUCT)" cannot have overlay in vendor tree: $(_vendor_package_overlays)) endif _vendor_package_overlays := _vendor_check_copy_files := $(filter-out $(_vendor_exception_path_prefix),\ $(filter vendor/%, $(PRODUCT_COPY_FILES))) ifneq (,$(_vendor_check_copy_files)) $(foreach c, $(_vendor_check_copy_files), \ $(if $(filter $(_vendor_owner_allowed_list), $(call word-colon,3,$(c))),,\ $(error Error: vendor PRODUCT_COPY_FILES file "$(c)" has unknown owner))\ $(eval _vendor_module_owner_info += $(call word-colon,2,$(c)):$(call word-colon,3,$(c)))) endif _vendor_check_copy_files := $(foreach m, $(_vendor_check_modules), \ $(if $(filter $(_vendor_owner_allowed_list), $(ALL_MODULES.$(m).OWNER)),,\ $(error Error: vendor module "$(m)" in $(ALL_MODULES.$(m).PATH) with unknown owner \ "$(ALL_MODULES.$(m).OWNER)" in product "$(TARGET_PRODUCT)"))\ $(if $(ALL_MODULES.$(m).INSTALLED),\ $(eval _vendor_module_owner_info += $(patsubst $(PRODUCT_OUT)/%,%,$(ALL_MODULES.$(m).INSTALLED)):$(ALL_MODULES.$(m).OWNER)))) endif # Restrict paths ifneq (,$(filter path all, $(_restrictions))) $(foreach m, $(_vendor_check_modules), \ $(if $(filter-out ,$(ALL_MODULES.$(m).INSTALLED)),\ $(if $(filter $(TARGET_OUT_VENDOR)/% $(TARGET_OUT_ODM)/% $(TARGET_OUT_VENDOR_DLKM)/% $(TARGET_OUT_ODM_DLKM)/% $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED)),,\ $(error Error: vendor module "$(m)" in $(ALL_MODULES.$(m).PATH) \ in product "$(TARGET_PRODUCT)" being installed to \ $(ALL_MODULES.$(m).INSTALLED) which is not in the vendor, odm, vendor_dlkm or odm_dlkm tree)))) endif _vendor_module_owner_info_txt := $(call intermediates-dir-for,PACKAGING,vendor_owner_info)/vendor_owner_info.txt $(_vendor_module_owner_info_txt): PRIVATE_INFO := $(_vendor_module_owner_info) $(_vendor_module_owner_info_txt): @echo "Write vendor module owner info $@" @mkdir -p $(dir $@) && rm -f $@ ifdef _vendor_module_owner_info @for w in $(PRIVATE_INFO); \ do \ echo $$w >> $@; \ done else @echo "No vendor module owner info." > $@ endif $(call dist-for-goals, droidcore, $(_vendor_module_owner_info_txt)) _vendor_module_owner_info_txt := _vendor_module_owner_info := _vendor_check_modules := _vendor_exception_path_prefix := _vendor_exception_modules := _restrictions := endif ================================================ FILE: core/tasks/vts-core-tests.mk ================================================ # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. test_suite_name := vts test_suite_tradefed := vts-tradefed test_suite_readme := test/vts/tools/vts-core-tradefed/README include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: vts vts: $(compatibility_zip) $(compatibility_tests_list_zip) $(call dist-for-goals, vts, $(compatibility_zip) $(compatibility_tests_list_zip)) tests: vts ================================================ FILE: core/tasks/with-license.mk ================================================ # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: with-license name := $(TARGET_PRODUCT) ifeq ($(TARGET_BUILD_TYPE),debug) name := $(name)_debug endif dist_name := $(name)-flashable-FILE_NAME_TAG_PLACEHOLDER-with-license name := $(name)-flashable-with-license with_license_intermediates := \ $(call intermediates-dir-for,PACKAGING,with_license) # Create a with-license artifact target license_image_input_zip := $(with_license_intermediates)/$(name).zip $(license_image_input_zip) : $(BUILT_TARGET_FILES_PACKAGE) $(ZIP2ZIP) # DO NOT PROCEED without a license file. ifndef VENDOR_BLOBS_LICENSE @echo "with-license requires VENDOR_BLOBS_LICENSE to be set." exit 1 else $(ZIP2ZIP) -i $(BUILT_TARGET_FILES_PACKAGE) -o $@ \ RADIO/bootloader.img:bootloader.img RADIO/radio.img:radio.img \ IMAGES/*.img:. OTA/android-info.txt:android-info.txt endif $(call declare-1p-container,$(license_image_input_zip),build) $(call declare-container-deps,$(license_image_input_zip),$(BUILT_TARGET_FILES_PACKAGE)) with_license_zip := $(PRODUCT_OUT)/$(name).sh dist_name := $(dist_name).sh $(with_license_zip): PRIVATE_NAME := $(name) $(with_license_zip): PRIVATE_INPUT_ZIP := $(license_image_input_zip) $(with_license_zip): PRIVATE_VENDOR_BLOBS_LICENSE := $(VENDOR_BLOBS_LICENSE) $(with_license_zip): $(license_image_input_zip) $(VENDOR_BLOBS_LICENSE) $(with_license_zip): $(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive # Args: $(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive $@ \ $(PRIVATE_INPUT_ZIP) $(PRIVATE_NAME) $(PRIVATE_VENDOR_BLOBS_LICENSE) with-license : $(with_license_zip) $(call dist-for-goals, with-license, $(with_license_zip):$(dist_name)) $(call declare-1p-container,$(with_license_zip),) $(call declare-container-license-deps,$(with_license_zip),$(license_image_input_zip),$(with_license_zip):) ================================================ FILE: core/tasks/wvts.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Widevine test suite for non-GMS partners: go/android-wvts ifneq ($(wildcard test/wvts/tools/wvts-tradefed/README),) test_suite_name := wvts test_suite_tradefed := wvts-tradefed test_suite_dynamic_config := test/wvts/tools/wvts-tradefed/DynamicConfig.xml test_suite_readme := test/wvts/tools/wvts-tradefed/README $(call declare-1p-target,$(test_suite_dynamic_config),wvts) $(call declare-1p-target,$(test_suite_readme),wvts) include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk .PHONY: wvts wvts: $(compatibility_zip) $(compatibility_tests_list_zip) $(call dist-for-goals, wvts, $(compatibility_zip) $(compatibility_tests_list_zip)) endif ================================================ FILE: core/use_lld_setup.mk ================================================ ############################################################# ## Set up flags based on LOCAL_USE_CLANG_LLD. ## Input variables: LOCAL_USE_CLANG_LLD ## Output variables: my_use_clang_lld ############################################################# # Use LLD by default. # Do not use LLD if LOCAL_USE_CLANG_LLD is false or 0 my_use_clang_lld := true ifneq (,$(LOCAL_USE_CLANG_LLD)) ifneq (,$(filter 0 false,$(LOCAL_USE_CLANG_LLD))) my_use_clang_lld := false endif endif # Do not use LLD for Darwin host executables or shared libraries. See # https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O. ifeq ($($(my_prefix)OS),darwin) my_use_clang_lld := false endif ================================================ FILE: core/version_util.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Handle various build version information. # # Guarantees that the following are defined: # PLATFORM_VERSION # PLATFORM_DISPLAY_VERSION # PLATFORM_SDK_VERSION # PLATFORM_SDK_EXTENSION_VERSION # PLATFORM_BASE_SDK_EXTENSION_VERSION # PLATFORM_VERSION_CODENAME # DEFAULT_APP_TARGET_SDK # BUILD_ID # BUILD_NUMBER # PLATFORM_SECURITY_PATCH # PLATFORM_SYSTEMSDK_VERSIONS # PLATFORM_VERSION_LAST_STABLE # PLATFORM_VERSION_KNOWN_CODENAMES # # Look for an optional file containing overrides of the defaults, # but don't cry if we don't find it. We could just use -include, but # the build.prop target also wants INTERNAL_BUILD_ID_MAKEFILE to be set # if the file exists. # INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk) ifdef INTERNAL_BUILD_ID_MAKEFILE include $(INTERNAL_BUILD_ID_MAKEFILE) endif ifdef TARGET_PLATFORM_VERSION $(error Do not set TARGET_PLATFORM_VERSION directly. Use RELEASE_PLATFORM_VERSION. value: $(TARGET_PLATFORM_VERSION)) endif TARGET_PLATFORM_VERSION := $(RELEASE_PLATFORM_VERSION) .KATI_READONLY := TARGET_PLATFORM_VERSION ifdef PLATFORM_SECURITY_PATCH $(error Do not set PLATFORM_SECURITY_PATCH directly. Use RELEASE_PLATFORM_SECURITY_PATCH. value: $(PLATFORM_SECURITY_PATCH)) endif PLATFORM_SECURITY_PATCH := $(RELEASE_PLATFORM_SECURITY_PATCH) .KATI_READONLY := PLATFORM_SECURITY_PATCH ifdef PLATFORM_SDK_VERSION $(error Do not set PLATFORM_SDK_VERSION directly. Use RELEASE_PLATFORM_SDK_VERSION. value: $(PLATFORM_SDK_VERSION)) endif PLATFORM_SDK_VERSION := $(RELEASE_PLATFORM_SDK_VERSION) .KATI_READONLY := PLATFORM_SDK_VERSION ifdef PLATFORM_SDK_MINOR_VERSION $(error Do not set PLATFORM_SDK_MINOR_VERSION directly. Use RELEASE_PLATFORM_SDK_MINOR_VERSION. value: $(PLATFORM_SDK_MINOR_VERSION)) endif PLATFORM_SDK_MINOR_VERSION := $(RELEASE_PLATFORM_SDK_MINOR_VERSION) .KATI_READONLY := PLATFORM_SDK_MINOR_VERSION ifdef PLATFORM_SDK_EXTENSION_VERSION $(error Do not set PLATFORM_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_SDK_EXTENSION_VERSION. value: $(PLATFORM_SDK_EXTENSION_VERSION)) endif PLATFORM_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_SDK_EXTENSION_VERSION) .KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION ifdef PLATFORM_BASE_SDK_EXTENSION_VERSION $(error Do not set PLATFORM_BASE_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION. value: $(PLATFORM_BASE_SDK_EXTENSION_VERSION)) endif ifdef RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION # This is the sdk extension version that PLATFORM_SDK_VERSION ships with. PLATFORM_BASE_SDK_EXTENSION_VERSION := $(RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION) else # Fallback to PLATFORM_SDK_EXTENSION_VERSION if RELEASE_PLATFORM_BASE_SDK_EXTENSION_VERSION is undefined. PLATFORM_BASE_SDK_EXTENSION_VERSION := $(PLATFORM_SDK_EXTENSION_VERSION) endif .KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION ifdef PLATFORM_VERSION_CODENAME $(error Do not set PLATFORM_VERSION_CODENAME directly. Use RELEASE_PLATFORM_VERSION. value: $(PLATFORM_VERSION_CODENAME)) endif PLATFORM_VERSION_CODENAME := $(RELEASE_PLATFORM_VERSION_CODENAME) .KATI_READONLY := PLATFORM_VERSION_CODENAME ifdef PLATFORM_VERSION_ALL_CODENAMES $(error Do not set PLATFORM_VERSION_ALL_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_ALL_CODENAMES. value: $(PLATFORM_VERSION_ALL_CODENAMES)) endif PLATFORM_VERSION_ALL_CODENAMES := $(RELEASE_PLATFORM_VERSION_ALL_CODENAMES) .KATI_READONLY := PLATFORM_VERSION_ALL_CODENAMES ifdef PLATFORM_VERSION_ALL_PREVIEW_CODENAMES $(error Do not set PLATFORM_VERSION_ALL_PREVIEW_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_ALL_PREVIEW_CODENAMES. value: $(PLATFORM_VERSION_ALL_PREVIEW_CODENAMES)) endif PLATFORM_VERSION_ALL_PREVIEW_CODENAMES := $(RELEASE_PLATFORM_VERSION_ALL_PREVIEW_CODENAMES) .KATI_READONLY := PLATFORM_VERSION_ALL_PREVIEW_CODENAMES ifdef PLATFORM_VERSION_LAST_STABLE $(error Do not set PLATFORM_VERSION_LAST_STABLE directly. Use RELEASE_PLATFORM_VERSION_LAST_STABLE. value: $(PLATFORM_VERSION_CODENAME)) endif PLATFORM_VERSION_LAST_STABLE := $(RELEASE_PLATFORM_VERSION_LAST_STABLE) .KATI_READONLY := PLATFORM_VERSION_LAST_STABLE ifdef PLATFORM_VERSION_KNOWN_CODENAMES $(error Do not set PLATFORM_VERSION_KNOWN_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES. value: $(PLATFORM_VERSION_KNOWN_CODENAMES)) endif PLATFORM_VERSION_KNOWN_CODENAMES := $(RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES) .KATI_READONLY := PLATFORM_VERSION_KNOWN_CODENAMES ifndef PLATFORM_VERSION ifeq (REL,$(PLATFORM_VERSION_CODENAME)) PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE) else PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME) endif endif .KATI_READONLY := PLATFORM_VERSION ifndef PLATFORM_DISPLAY_VERSION PLATFORM_DISPLAY_VERSION := $(PLATFORM_VERSION) endif .KATI_READONLY := PLATFORM_DISPLAY_VERSION ifeq (REL,$(PLATFORM_VERSION_CODENAME)) PLATFORM_PREVIEW_SDK_VERSION := 0 else ifndef PLATFORM_PREVIEW_SDK_VERSION # This is the definition of a preview SDK version over and above the current # platform SDK version. Unlike the platform SDK version, a higher value # for preview SDK version does NOT mean that all prior preview APIs are # included. Packages reading this value to determine compatibility with # known APIs should check that this value is precisely equal to the preview # SDK version the package was built for, otherwise it should fall back to # assuming the device can only support APIs as of the previous official # public release. # This value will always be forced to 0 for release builds by the logic # in the "ifeq" block above, so the value below will be used on any # non-release builds, and it should always be at least 1, to indicate that # APIs may have changed since the claimed PLATFORM_SDK_VERSION. PLATFORM_PREVIEW_SDK_VERSION := 1 endif endif .KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION ifndef DEFAULT_APP_TARGET_SDK # This is the default minSdkVersion and targetSdkVersion to use for # all .apks created by the build system. It can be overridden by explicitly # setting these in the .apk's AndroidManifest.xml. It is either the code # name of the development build or, if this is a release build, the official # SDK version of this release. ifeq (REL,$(PLATFORM_VERSION_CODENAME)) DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION) else DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME) endif endif .KATI_READONLY := DEFAULT_APP_TARGET_SDK ifndef PLATFORM_SYSTEMSDK_MIN_VERSION # This is the oldest version of system SDK that the platform supports. Contrary # to the public SDK where platform essentially supports all previous SDK versions, # platform supports only a few number of recent system SDK versions as some of # old system APIs are gradually deprecated, removed and then deleted. PLATFORM_SYSTEMSDK_MIN_VERSION := 29 endif .KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION # This is the list of system SDK versions that the current platform supports. PLATFORM_SYSTEMSDK_VERSIONS := ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION)) $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\ $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION))) PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION)) endif # Platform always supports the current version ifeq (REL,$(PLATFORM_VERSION_CODENAME)) PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION) else PLATFORM_SYSTEMSDK_VERSIONS += $(subst $(comma),$(space),$(PLATFORM_VERSION_ALL_CODENAMES)) endif PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS))) .KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS .KATI_READONLY := PLATFORM_SECURITY_PATCH ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH. PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s) endif .KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP # PLATFORM_BASE_OS is used to indicate the base os applied # to the device. Can be an arbitrary string, but must be a # single word. # # If there is no $PLATFORM_BASE_OS set, keep it empty. # # PLATFORM_BASE_OS can either be set via an enviornment # variable, or set via the PRODUCT_BASE_OS product variable. PLATFORM_BASE_OS_ENV_INPUT := $(PLATFORM_BASE_OS) .KATI_READONLY := PLATFORM_BASE_OS_ENV_INPUT PLATFORM_BASE_OS := ifndef BUILD_ID # Used to signify special builds. E.g., branches and/or releases, # like "M5-RC7". Can be an arbitrary string, but must be a single # word and a valid file name. # # If there is no BUILD_ID set, make it obvious. BUILD_ID := UNKNOWN endif .KATI_READONLY := BUILD_ID ifndef BUILD_DATETIME # Used to reproduce builds by setting the same time. Must be the number # of seconds since the Epoch. BUILD_DATETIME := $(shell date +%s) endif DATE := date -d @$(BUILD_DATETIME) .KATI_READONLY := DATE # Everything should be using BUILD_DATETIME_FROM_FILE instead. # BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves # to soong_ui. $(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE) ifndef HAS_BUILD_NUMBER HAS_BUILD_NUMBER := false endif .KATI_READONLY := HAS_BUILD_NUMBER ifdef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION $(error Do not set PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION directly. Use RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION. value: $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)) endif PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := $(RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION) .KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION ================================================ FILE: envsetup.sh ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # gettop is duplicated here and in shell_utils.mk, because it's difficult # to find shell_utils.make without it for all the novel ways this file can be # sourced. Other common functions should only be in one place or the other. function _gettop_once { local TOPFILE=build/make/core/envsetup.mk if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then # The following circumlocution ensures we remove symlinks from TOP. (cd "$TOP"; PWD= /bin/pwd) else if [ -f $TOPFILE ] ; then # The following circumlocution (repeated below as well) ensures # that we record the true directory name and not one that is # faked up with symlink names. PWD= /bin/pwd else local HERE=$PWD local T= while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do \cd .. T=`PWD= /bin/pwd -P` done \cd "$HERE" if [ -f "$T/$TOPFILE" ]; then echo "$T" fi fi fi } T=$(_gettop_once) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Always source build/envsetup.sh from the root of the tree." >&2 return 1 fi IMPORTING_ENVSETUP=true source $T/build/make/shell_utils.sh # Get all the build variables needed by this script in a single call to the build system. function build_build_var_cache() { local T=$(gettop) # Grep out the variable names from the script. cached_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/_get_build_var_cached/) print $(i+1)}' | sort -u | tr '\n' ' '`) cached_abs_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/_get_abs_build_var_cached/) print $(i+1)}' | sort -u | tr '\n' ' '`) # Call the build system to dump the "=" pairs as a shell script. build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \ --vars="${cached_vars[*]}" \ --abs-vars="${cached_abs_vars[*]}" \ --var-prefix=var_cache_ \ --abs-var-prefix=abs_var_cache_` local ret=$? if [ $ret -ne 0 ] then unset build_dicts_script return $ret fi # Execute the script to store the "=" pairs as shell variables. eval "$build_dicts_script" ret=$? unset build_dicts_script if [ $ret -ne 0 ] then return $ret fi BUILD_VAR_CACHE_READY="true" } # Delete the build var cache, so that we can still call into the build system # to get build variables not listed in this script. function destroy_build_var_cache() { unset BUILD_VAR_CACHE_READY local v for v in $cached_vars; do unset var_cache_$v done unset cached_vars for v in $cached_abs_vars; do unset abs_var_cache_$v done unset cached_abs_vars } # Get the value of a build variable as an absolute path. function _get_abs_build_var_cached() { if [ "$BUILD_VAR_CACHE_READY" = "true" ] then eval "echo \"\${abs_var_cache_$1}\"" return fi local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." >&2 return fi (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1) } # Get the exact value of a build variable. function _get_build_var_cached() { if [ "$BUILD_VAR_CACHE_READY" = "true" ] then eval "echo \"\${var_cache_$1}\"" return 0 fi local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." >&2 return 1 fi (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1) } # This logic matches envsetup.mk function get_host_prebuilt_prefix { local un=$(uname) if [[ $un == "Linux" ]] ; then echo linux-x86 elif [[ $un == "Darwin" ]] ; then echo darwin-x86 else echo "Error: Invalid host operating system: $un" 1>&2 fi } # Add directories to PATH that are dependent on the lunch target. # For directories that are not lunch-specific, add them in set_global_paths function set_lunch_paths() { local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." return fi ################################################################## # # # Read me before you modify this code # # # # This function sets ANDROID_LUNCH_BUILD_PATHS to what it is # # adding to PATH, and the next time it is run, it removes that # # from PATH. This is required so lunch can be run more than # # once and still have working paths. # # # ################################################################## # Note: on windows/cygwin, ANDROID_LUNCH_BUILD_PATHS will contain spaces # due to "C:\Program Files" being in the path. # Handle compat with the old ANDROID_BUILD_PATHS variable. # TODO: Remove this after we think everyone has lunched again. if [ -z "$ANDROID_LUNCH_BUILD_PATHS" -a -n "$ANDROID_BUILD_PATHS" ] ; then ANDROID_LUNCH_BUILD_PATHS="$ANDROID_BUILD_PATHS" ANDROID_BUILD_PATHS= fi if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/} # strip leading ':', if any export PATH=${PATH/:%/} ANDROID_PRE_BUILD_PATHS= fi # Out with the old... if [ -n "$ANDROID_LUNCH_BUILD_PATHS" ] ; then export PATH=${PATH/$ANDROID_LUNCH_BUILD_PATHS/} fi # And in with the new... ANDROID_LUNCH_BUILD_PATHS=$(_get_abs_build_var_cached SOONG_HOST_OUT_EXECUTABLES) ANDROID_LUNCH_BUILD_PATHS+=:$(_get_abs_build_var_cached HOST_OUT_EXECUTABLES) # Append llvm binutils prebuilts path to ANDROID_LUNCH_BUILD_PATHS. local ANDROID_LLVM_BINUTILS=$(_get_abs_build_var_cached ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_LLVM_BINUTILS # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds. export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer # Append asuite prebuilts path to ANDROID_LUNCH_BUILD_PATHS. local os_arch=$(_get_build_var_cached HOST_PREBUILT_TAG) ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/acloud/$os_arch ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/aidegen/$os_arch ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/atest/$os_arch export ANDROID_JAVA_HOME=$(_get_abs_build_var_cached ANDROID_JAVA_HOME) export JAVA_HOME=$ANDROID_JAVA_HOME export ANDROID_JAVA_TOOLCHAIN=$(_get_abs_build_var_cached ANDROID_JAVA_TOOLCHAIN) ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_JAVA_TOOLCHAIN # Fix up PYTHONPATH if [ -n $ANDROID_PYTHONPATH ]; then export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/} fi # //development/python-packages contains both a pseudo-PYTHONPATH which # mimics an already assembled venv, but also contains real Python packages # that are not in that layout until they are installed. We can fake it for # the latter type by adding the package source directories to the PYTHONPATH # directly. For the former group, we only need to add the python-packages # directory itself. # # This could be cleaned up by converting the remaining packages that are in # the first category into a typical python source layout (that is, another # layer of directory nesting) and automatically adding all subdirectories of # python-packages to the PYTHONPATH instead of manually curating this. We # can't convert the packages like adb to the other style because doing so # would prevent exporting type info from those packages. # # http://b/266688086 export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages/gdbrunner:$T/development/python-packages: if [ -n $VENDOR_PYTHONPATH ]; then ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH fi export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH unset ANDROID_PRODUCT_OUT export ANDROID_PRODUCT_OUT=$(_get_abs_build_var_cached PRODUCT_OUT) export OUT=$ANDROID_PRODUCT_OUT unset ANDROID_HOST_OUT export ANDROID_HOST_OUT=$(_get_abs_build_var_cached HOST_OUT) unset ANDROID_SOONG_HOST_OUT export ANDROID_SOONG_HOST_OUT=$(_get_abs_build_var_cached SOONG_HOST_OUT) unset ANDROID_HOST_OUT_TESTCASES export ANDROID_HOST_OUT_TESTCASES=$(_get_abs_build_var_cached HOST_OUT_TESTCASES) unset ANDROID_TARGET_OUT_TESTCASES export ANDROID_TARGET_OUT_TESTCASES=$(_get_abs_build_var_cached TARGET_OUT_TESTCASES) # Finally, set PATH export PATH=$ANDROID_LUNCH_BUILD_PATHS:$PATH } # Add directories to PATH that are NOT dependent on the lunch target. # For directories that are lunch-specific, add them in set_lunch_paths function set_global_paths() { local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." return fi ################################################################## # # # Read me before you modify this code # # # # This function sets ANDROID_GLOBAL_BUILD_PATHS to what it is # # adding to PATH, and the next time it is run, it removes that # # from PATH. This is required so envsetup.sh can be sourced # # more than once and still have working paths. # # # ################################################################## # Out with the old... if [ -n "$ANDROID_GLOBAL_BUILD_PATHS" ] ; then export PATH=${PATH/$ANDROID_GLOBAL_BUILD_PATHS/} fi # And in with the new... ANDROID_GLOBAL_BUILD_PATHS=$T/build/soong/bin ANDROID_GLOBAL_BUILD_PATHS+=:$T/build/bazel/bin ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools # add kernel specific binaries if [ $(uname -s) = Linux ] ; then ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/dtc ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/libufdt fi # If prebuilts/android-emulator// exists, prepend it to our PATH # to ensure that the corresponding 'emulator' binaries are used. case $(uname -s) in Darwin) ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64 ;; Linux) ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64 ;; *) ANDROID_EMULATOR_PREBUILTS= ;; esac if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then ANDROID_GLOBAL_BUILD_PATHS+=:$ANDROID_EMULATOR_PREBUILTS export ANDROID_EMULATOR_PREBUILTS fi # Finally, set PATH export PATH=$ANDROID_GLOBAL_BUILD_PATHS:$PATH } function printconfig() { local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." >&2 return fi _get_build_var_cached report_config } function set_stuff_for_environment() { set_lunch_paths set_sequence_number export ANDROID_BUILD_TOP=$(gettop) } function set_sequence_number() { export BUILD_ENV_SEQUENCE_NUMBER=13 } # Takes a command name, and check if it's in ENVSETUP_NO_COMPLETION or not. function should_add_completion() { local cmd="$(basename $1| sed 's/_completion//' |sed 's/\.\(.*\)*sh$//')" case :"$ENVSETUP_NO_COMPLETION": in *:"$cmd":*) return 1 ;; esac return 0 } function addcompletions() { local f= # Keep us from trying to run in something that's neither bash nor zsh. if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then return fi # Keep us from trying to run in bash that's too old. if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then return fi local completion_files=( packages/modules/adb/adb.bash system/core/fastboot/fastboot.bash tools/asuite/asuite.sh ) # Completion can be disabled selectively to allow users to use non-standard completion. # e.g. # ENVSETUP_NO_COMPLETION=adb # -> disable adb completion # ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion local T=$(gettop) for f in ${completion_files[*]}; do f="$T/$f" if [ ! -f "$f" ]; then echo "Warning: completion file $f not found" elif should_add_completion "$f"; then . $f fi done if [ -z "$ZSH_VERSION" ]; then # Doesn't work in zsh. complete -o nospace -F _croot croot # TODO(b/244559459): Support b autocompletion for zsh complete -F _bazel__complete -o nospace b fi complete -F _lunch lunch complete -F _lunch_completion lunch2 complete -F _complete_android_module_names pathmod complete -F _complete_android_module_names gomod complete -F _complete_android_module_names outmod complete -F _complete_android_module_names installmod complete -F _complete_android_module_names m } function add_lunch_combo() { if [ -n "$ZSH_VERSION" ]; then echo -n "${funcfiletrace[1]}: " else echo -n "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: " fi echo "add_lunch_combo is obsolete. Use COMMON_LUNCH_CHOICES in your AndroidProducts.mk instead." } function print_lunch_menu() { local uname=$(uname) local choices choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_RELEASE= TARGET_BUILD_VARIANT= _get_build_var_cached COMMON_LUNCH_CHOICES 2>/dev/null) local ret=$? echo echo "You're building on" $uname echo if [ $ret -ne 0 ] then echo "Warning: Cannot display lunch menu." echo echo "Note: You can invoke lunch with an explicit target:" echo echo " usage: lunch [target]" >&2 echo return fi echo "Lunch menu .. Here are the common combinations:" local i=1 local choice for choice in $(echo $choices) do echo " $i. $choice" i=$(($i+1)) done echo } function _lunch_meat() { local product=$1 local release=$2 local variant=$3 TARGET_PRODUCT=$product \ TARGET_RELEASE=$release \ TARGET_BUILD_VARIANT=$variant \ build_build_var_cache if [ $? -ne 0 ] then if [[ "$product" =~ .*_(eng|user|userdebug) ]] then echo "Did you mean -${product/*_/}? (dash instead of underscore)" fi return 1 fi export TARGET_PRODUCT=$(_get_build_var_cached TARGET_PRODUCT) export TARGET_BUILD_VARIANT=$(_get_build_var_cached TARGET_BUILD_VARIANT) export TARGET_RELEASE=$release # Note this is the string "release", not the value of the variable. export TARGET_BUILD_TYPE=release [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || echo set_stuff_for_environment [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || printconfig if [[ -z "${ANDROID_QUIET_BUILD}" ]]; then local spam_for_lunch=$(gettop)/build/make/tools/envsetup/spam_for_lunch if [[ -x $spam_for_lunch ]]; then $spam_for_lunch fi fi destroy_build_var_cache if [[ -n "${CHECK_MU_CONFIG:-}" ]]; then check_mu_config fi } unset COMMON_LUNCH_CHOICES_CACHE # Tab completion for lunch. function _lunch() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ -z "$COMMON_LUNCH_CHOICES_CACHE" ]; then COMMON_LUNCH_CHOICES_CACHE=$(TARGET_BUILD_APPS= _get_build_var_cached COMMON_LUNCH_CHOICES) fi COMPREPLY=( $(compgen -W "${COMMON_LUNCH_CHOICES_CACHE}" -- ${cur}) ) return 0 } function _lunch_usage() { ( echo "The lunch command selects the configuration to use for subsequent" echo "Android builds." echo echo "Usage: lunch TARGET_PRODUCT [TARGET_RELEASE [TARGET_BUILD_VARIANT]]" echo echo " Choose the product, release and variant to use. If not" echo " supplied, TARGET_RELEASE will be 'trunk_staging' and" echo " TARGET_BUILD_VARIANT will be 'eng'" echo echo echo "Usage: lunch TARGET_PRODUCT-TARGET_RELEASE-TARGET_BUILD_VARIANT" echo echo " Chose the product, release and variant to use. This" echo " legacy format is maintained for compatibility." echo echo echo "Note that the previous interactive menu and list of hard-coded" echo "list of curated targets has been removed. If you would like the" echo "list of products, release configs for a particular product, or" echo "variants, run the following as individual commands:" echo "list_products, list_releases, or list_variants" echo "respectively." echo ) 1>&2 } function lunch() { if [[ $# -eq 1 && $1 = "--help" ]]; then _lunch_usage return 0 fi if [[ $# -eq 0 ]]; then echo "No target specified. See lunch --help" 1>&2 return 1 fi if [[ $# -gt 3 ]]; then echo "Too many parameters given. See lunch --help" 1>&2 return 1 fi local product release variant # Handle the legacy format local legacy=$(echo $1 | grep "-") if [[ $# -eq 1 && -n $legacy ]]; then IFS="-" read -r product release variant <<< "$1" if [[ -z "$product" ]] || [[ -z "$release" ]] || [[ -z "$variant" ]]; then echo "Invalid lunch combo: $1" 1>&2 echo "Valid combos must be of the form -- when using" 1>&2 echo "the legacy format. Run 'lunch --help' for usage." 1>&2 return 1 fi fi # Handle the new format. if [[ -z $legacy ]]; then product=$1 release=$2 if [[ -z $release ]]; then release=trunk_staging fi variant=$3 if [[ -z $variant ]]; then variant=eng fi fi # Validate the selection and set all the environment stuff _lunch_meat $product $release $variant } unset ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE unset ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT unset ANDROID_LUNCH_COMPLETION_RELEASE_CACHE # Tab completion for lunch. function _lunch_completion() { # Available products if [[ $COMP_CWORD -eq 1 ]] ; then if [[ -z $ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE ]]; then ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE=$(list_products) fi COMPREPLY=( $(compgen -W "${ANDROID_LUNCH_COMPLETION_PRODUCT_CACHE}" -- "${COMP_WORDS[COMP_CWORD]}") ) fi # Available release configs if [[ $COMP_CWORD -eq 2 ]] ; then if [[ -z $ANDROID_LUNCH_COMPLETION_RELEASE_CACHE || $ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT != ${COMP_WORDS[1]} ]] ; then ANDROID_LUNCH_COMPLETION_RELEASE_CACHE=$(list_releases ${COMP_WORDS[1]}) ANDROID_LUNCH_COMPLETION_CHOSEN_PRODUCT=${COMP_WORDS[1]} fi COMPREPLY=( $(compgen -W "${ANDROID_LUNCH_COMPLETION_RELEASE_CACHE}" -- "${COMP_WORDS[COMP_CWORD]}") ) fi # Available variants if [[ $COMP_CWORD -eq 3 ]] ; then COMPREPLY=(user userdebug eng) fi return 0 } # Configures the build to build unbundled apps. # Run tapas with one or more app names (from LOCAL_PACKAGE_NAME) function tapas() { local showHelp="$(echo $* | xargs -n 1 echo | \grep -E '^(help)$' | xargs)" local arch="$(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|arm64|x86_64)$' | xargs)" # TODO(b/307975293): Expand tapas to take release arguments (and update hmm() usage). local release="trunk_staging" local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)" local density="$(echo $* | xargs -n 1 echo | \grep -E '^(ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi)$' | xargs)" local keys="$(echo $* | xargs -n 1 echo | \grep -E '^(devkeys)$' | xargs)" local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|arm|x86|arm64|x86_64|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi|devkeys)$' | xargs)" if [ "$showHelp" != "" ]; then $(gettop)/build/make/tapasHelp.sh return fi if [ $(echo $arch | wc -w) -gt 1 ]; then echo "tapas: Error: Multiple build archs supplied: $arch" return fi if [ $(echo $release | wc -w) -gt 1 ]; then echo "tapas: Error: Multiple build releases supplied: $release" return fi if [ $(echo $variant | wc -w) -gt 1 ]; then echo "tapas: Error: Multiple build variants supplied: $variant" return fi if [ $(echo $density | wc -w) -gt 1 ]; then echo "tapas: Error: Multiple densities supplied: $density" return fi if [ $(echo $keys | wc -w) -gt 1 ]; then echo "tapas: Error: Multiple keys supplied: $keys" return fi local product=aosp_arm case $arch in x86) product=aosp_x86;; arm64) product=aosp_arm64;; x86_64) product=aosp_x86_64;; esac if [ -n "$keys" ]; then product=${product/aosp_/aosp_${keys}_} fi; if [ -z "$variant" ]; then variant=eng fi if [ -z "$apps" ]; then apps=all fi if [ -z "$density" ]; then density=alldpi fi export TARGET_PRODUCT=$product export TARGET_RELEASE=$release export TARGET_BUILD_VARIANT=$variant export TARGET_BUILD_DENSITY=$density export TARGET_BUILD_TYPE=release export TARGET_BUILD_APPS=$apps build_build_var_cache set_stuff_for_environment printconfig destroy_build_var_cache } # Configures the build to build unbundled Android modules (APEXes). # Run banchan with one or more module names (from apex{} modules). function banchan() { local showHelp="$(echo $* | xargs -n 1 echo | \grep -E '^(help)$' | xargs)" local product="$(echo $* | xargs -n 1 echo | \grep -E '^(.*_)?(arm|x86|arm64|riscv64|x86_64|arm64only|x86_64only)$' | xargs)" # TODO: Expand banchan to take release arguments (and update hmm() usage). local release="trunk_staging" local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)" local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|(.*_)?(arm|x86|arm64|riscv64|x86_64))$' | xargs)" if [ "$showHelp" != "" ]; then $(gettop)/build/make/banchanHelp.sh return fi if [ -z "$product" ]; then product=arm64 elif [ $(echo $product | wc -w) -gt 1 ]; then echo "banchan: Error: Multiple build archs or products supplied: $products" return fi if [ $(echo $release | wc -w) -gt 1 ]; then echo "banchan: Error: Multiple build releases supplied: $release" return fi if [ $(echo $variant | wc -w) -gt 1 ]; then echo "banchan: Error: Multiple build variants supplied: $variant" return fi if [ -z "$apps" ]; then echo "banchan: Error: No modules supplied" return fi case $product in arm) product=module_arm;; x86) product=module_x86;; arm64) product=module_arm64;; riscv64) product=module_riscv64;; x86_64) product=module_x86_64;; arm64only) product=module_arm64only;; x86_64only) product=module_x86_64only;; esac if [ -z "$variant" ]; then variant=eng fi export TARGET_PRODUCT=$product export TARGET_RELEASE=$release export TARGET_BUILD_VARIANT=$variant export TARGET_BUILD_DENSITY=alldpi export TARGET_BUILD_TYPE=release # This setup currently uses TARGET_BUILD_APPS just like tapas, but the use # case is different and it may diverge in the future. export TARGET_BUILD_APPS=$apps build_build_var_cache set_stuff_for_environment printconfig destroy_build_var_cache } function croot() { local T=$(gettop) if [ "$T" ]; then if [ "$1" ]; then \cd $(gettop)/$1 else \cd $(gettop) fi else echo "Couldn't locate the top of the tree. Try setting TOP." fi } function _croot() { local T=$(gettop) if [ "$T" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" k=0 for c in $(compgen -d ${T}/${cur}); do COMPREPLY[k++]=${c#${T}/}/ done fi } function cproj() { local TOPFILE=build/make/core/envsetup.mk local HERE=$PWD local T= while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do T=$PWD if [ -f "$T/Android.mk" ]; then \cd $T return fi \cd .. done \cd $HERE echo "can't find Android.mk" } # Ensure that we're always using the adb in the tree. This works around the fact # that bash caches $PATH lookups, so if you use adb before lunching/building the # one in your tree, you'll continue to get /usr/bin/adb or whatever even after # you have the one from your current tree on your path. Historically this would # cause confusion because glinux had adb in /usr/bin/ by default, though that # doesn't appear to be the case on my rodete hosts; it is however still the case # that my Mac has /usr/local/bin/adb installed by default and on the default # path. function adb() { # We need `command which` because zsh has a built-in `which` that's more # like `type`. local ADB=$(command which adb) if [ -z "$ADB" ]; then echo "Command adb not found; try lunch (and building) first?" return 1 fi run_tool_with_logging "ADB" $ADB "${@}" } function fastboot() { local FASTBOOT=$(command which fastboot) if [ -z "$FASTBOOT" ]; then echo "Command fastboot not found; try lunch (and building) first?" return 1 fi # Support tool event logging for fastboot command. run_tool_with_logging "FASTBOOT" $FASTBOOT "${@}" } # communicate with a running device or emulator, set up necessary state, # and run the hat command. function runhat() { # process standard adb options local adbTarget="" if [ "$1" = "-d" -o "$1" = "-e" ]; then adbTarget=$1 shift 1 elif [ "$1" = "-s" ]; then adbTarget="$1 $2" shift 2 fi local adbOptions=${adbTarget} #echo adbOptions = ${adbOptions} # runhat options local targetPid=$1 if [ "$targetPid" = "" ]; then echo "Usage: runhat [ -d | -e | -s serial ] target-pid" return fi # confirm hat is available if [ -z $(which hat) ]; then echo "hat is not available in this configuration." return fi # issue "am" command to cause the hprof dump local devFile=/data/local/tmp/hprof-$targetPid echo "Poking $targetPid and waiting for data..." echo "Storing data at $devFile" adb ${adbOptions} shell am dumpheap $targetPid $devFile echo "Press enter when logcat shows \"hprof: heap dump completed\"" echo -n "> " read local localFile=/tmp/$$-hprof echo "Retrieving file $devFile..." adb ${adbOptions} pull $devFile $localFile adb ${adbOptions} shell rm $devFile echo "Running hat on $localFile" echo "View the output by pointing your browser at http://localhost:7000/" echo "" hat -JXmx512m $localFile } function godir () { if [[ -z "$1" ]]; then echo "Usage: godir " return fi local T=$(gettop) local FILELIST if [ ! "$OUT_DIR" = "" ]; then mkdir -p $OUT_DIR FILELIST=$OUT_DIR/filelist else FILELIST=$T/filelist fi if [[ ! -f $FILELIST ]]; then echo -n "Creating index..." (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST) echo " Done" echo "" fi local lines lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq)) if [[ ${#lines[@]} = 0 ]]; then echo "Not found" return fi local pathname local choice if [[ ${#lines[@]} > 1 ]]; then while [[ -z "$pathname" ]]; do local index=1 local line for line in ${lines[@]}; do printf "%6s %s\n" "[$index]" $line index=$(($index + 1)) done echo echo -n "Select one: " unset choice read choice if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then echo "Invalid choice" continue fi pathname=${lines[@]:$(($choice-1)):1} done else pathname=${lines[@]:0:1} fi \cd $T/$pathname } # Go to a specific module in the android tree, as cached in module-info.json. If any build change # is made, and it should be reflected in the output, you should run 'refreshmod' first. # Note: This function is in envsetup because changing the directory needs to happen in the current # shell. All other functions that use module-info.json should be in build/soong/bin. function gomod() { if [[ $# -ne 1 ]]; then echo "usage: gomod " >&2 return 1 fi local path="$(pathmod $@)" if [ -z "$path" ]; then return 1 fi cd $path } function _complete_android_module_names() { local word=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $(allmod | grep -E "^$word") ) } function get_make_command() { # If we're in the top of an Android tree, use soong_ui.bash instead of make if [ -f build/soong/soong_ui.bash ]; then # Always use the real make if -C is passed in for arg in "$@"; do if [[ $arg == -C* ]]; then echo command make return fi done echo build/soong/soong_ui.bash --make-mode else echo command make fi } function make() { _wrap_build $(get_make_command "$@") "$@" } # Zsh needs bashcompinit called to support bash-style completion. function enable_zsh_completion() { # Don't override user's options if bash-style completion is already enabled. if ! declare -f complete >/dev/null; then autoload -U compinit && compinit autoload -U bashcompinit && bashcompinit fi } function validate_current_shell() { local current_sh="$(ps -o command -p $$)" case "$current_sh" in *bash*) function check_type() { type -t "$1"; } ;; *zsh*) function check_type() { type "$1"; } enable_zsh_completion ;; *) echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results." ;; esac } # Execute the contents of any vendorsetup.sh files we can find. # Unless we find an allowed-vendorsetup_sh-files file, in which case we'll only # load those. # # This allows loading only approved vendorsetup.sh files function source_vendorsetup() { unset VENDOR_PYTHONPATH local T="$(gettop)" allowed= for f in $(cd "$T" && find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do if [ -n "$allowed" ]; then echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:" echo " $allowed" echo " $f" return fi allowed="$T/$f" done allowed_files= [ -n "$allowed" ] && allowed_files=$(cat "$allowed") for dir in device vendor product; do for f in $(cd "$T" && test -d $dir && \ find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then echo "including $f"; . "$T/$f" else echo "ignoring $f, not in $allowed" fi done done setup_cog_env_if_needed } function showcommands() { local T=$(gettop) if [[ -z "$TARGET_PRODUCT" ]]; then >&2 echo "TARGET_PRODUCT not set. Run lunch." return fi case $(uname -s) in Darwin) PREBUILT_NAME=darwin-x86 ;; Linux) PREBUILT_NAME=linux-x86 ;; *) >&2 echo Unknown host $(uname -s) return ;; esac OUT_DIR="$(_get_abs_build_var_cached OUT_DIR)" if [[ "$1" == "--regenerate" ]]; then shift 1 NINJA_ARGS="-t commands $@" m else (cd $T && prebuilts/build-tools/$PREBUILT_NAME/bin/ninja \ -f $OUT_DIR/combined-${TARGET_PRODUCT}.ninja \ -t commands "$@") fi } # These functions used to be here but are now standalone scripts # in build/soong/bin. Unset these for the time being so the real # script is picked up. # TODO: Remove this some time after a suitable delay (maybe 2025?) unset allmod unset aninja unset cgrep unset core unset coredump_enable unset coredump_setup unset dirmods unset get_build_var unset get_abs_build_var unset getlastscreenshot unset getprebuilt unset getscreenshotpath unset getsdcardpath unset gettargetarch unset ggrep unset gogrep unset hmm unset installmod unset is64bit unset isviewserverstarted unset jgrep unset jsongrep unset key_back unset key_home unset key_menu unset ktgrep unset m unset mangrep unset mgrep unset mm unset mma unset mmm unset mmma unset outmod unset overrideflags unset owngrep unset pathmod unset pez unset pygrep unset qpid unset rcgrep unset refreshmod unset resgrep unset rsgrep unset run_tool_with_logging unset sepgrep unset sgrep unset startviewserver unset stopviewserver unset systemstack unset syswrite unset tomlgrep unset treegrep validate_current_shell set_global_paths source_vendorsetup addcompletions ================================================ FILE: help.sh ================================================ #!/bin/bash # locate some directories cd "$(dirname $0)" SCRIPT_DIR="${PWD}" cd ../.. TOP="${PWD}" message='The basic Android build process is: cd '"${TOP}"' source build/envsetup.sh # Add "lunch" (and other utilities and variables) # to the shell environment. lunch [-] # Choose the device to target. m [] # Execute the configured build. Usage of "m" imitates usage of the program "make". See '"${SCRIPT_DIR}"'/Usage.txt for more info about build usage and concepts. The parallelism of the build can be set with a -jN argument to "m". If you don'\''t provide a -j argument, the build system automatically selects a parallel task count that it thinks is optimal for your system. Common goals are: clean (aka clobber) equivalent to rm -rf out/ checkbuild Build every module defined in the source tree droid Default target sync Build everything in the default target except the images, for use with adb sync. nothing Do not build anything, just parse and validate the build structure java Build all the java code in the source tree native Build all the native code in the source tree host Build all the host code (not to be run on a device) in the source tree target Build all the target code (to be run on the device) in the source tree (java|native)-(host|target) (host|target)-(java|native) Build the intersection of the two given arguments snod Quickly rebuild the system image from built packages Stands for "System, NO Dependencies" vnod Quickly rebuild the vendor image from built packages Stands for "Vendor, NO Dependencies" pnod Quickly rebuild the product image from built packages Stands for "Product, NO Dependencies" senod Quickly rebuild the system_ext image from built packages Stands for "SystemExt, NO Dependencies" onod Quickly rebuild the odm image from built packages Stands for "Odm, NO Dependencies" vdnod Quickly rebuild the vendor_dlkm image from built packages Stands for "VendorDlkm, NO Dependencies" odnod Quickly rebuild the odm_dlkm image from built packages Stands for "OdmDlkm, NO Dependencies" sdnod Quickly rebuild the system_dlkm image from built packages Stands for "SystemDlkm, NO Dependencies" So, for example, you could run: cd '"${TOP}"' source build/envsetup.sh lunch aosp_arm-userdebug m -j java to build all of the java code for the userdebug variant of the aosp_arm device. ' echo "$message" ================================================ FILE: navbar.md ================================================ * [Home](/README.md) * [Usage](/Usage.txt) * [Changes](/Changes.md) * [Outdated Reference](/core/build-system.html) ================================================ FILE: packaging/distdir.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # From the Android.mk pass: DIST_GOAL_OUTPUT_PAIRS := DIST_SRC_DST_PAIRS := include $(KATI_PACKAGE_MK_DIR)/dist.mk FILE_NAME_TAG := $(file <$(OUT_DIR)/file_name_tag.txt) .KATI_READONLY := FILE_NAME_TAG $(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS), \ $(eval goal := $(call word-colon,1,$(pair))) \ $(eval output := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(call word-colon,2,$(pair)))) \ $(eval .PHONY: _dist_$$(goal)) \ $(if $(call streq,$(DIST),true),\ $(eval _dist_$$(goal): $$(DIST_DIR)/$$(output)), \ $(eval _dist_$$(goal):))) define copy-one-dist-file $(2): .KATI_TAGS += ;rule_name=dist-cp $(2): $(1) @echo "Dist: $$@" rm -f $$@ cp $$< $$@ endef ifeq ($(DIST),true) $(foreach pair,$(DIST_SRC_DST_PAIRS), \ $(eval src := $(call word-colon,1,$(pair))) \ $(eval dst := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(DIST_DIR)/$(call word-colon,2,$(pair)))) \ $(eval $(call copy-one-dist-file,$(src),$(dst)))) endif copy-one-dist-file := ================================================ FILE: packaging/main.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Create a default rule. This is unused currently, as the real default rule is # still in the Kati build step. .PHONY: _packaging_default_rule_ _packaging_default_rule_: ifndef KATI $(error Only Kati is supported.) endif $(info [1/3] initializing packaging system ...) .KATI_READONLY := KATI_PACKAGE_MK_DIR include build/make/common/core.mk include build/make/common/strings.mk $(info [2/3] including distdir.mk ...) include build/make/packaging/distdir.mk $(info [3/3] writing packaging rules ...) ================================================ FILE: packaging/main_soong_only.mk ================================================ # Copyright (C) 2025 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ifndef KATI $(error Only Kati is supported.) endif $(info [1/4] initializing packaging system ...) .KATI_READONLY := KATI_PACKAGE_MK_DIR include build/make/common/core.mk include build/make/common/strings.mk # Define well-known goals and their dependency graph that they've # traditionally had in make builds. Also it's important to define # droid first so that it's built by default. .PHONY: droid droid: droid_targets .PHONY: droid_targets droid_targets: droidcore dist_files .PHONY: dist_files dist_files: .PHONY: droidcore droidcore: droidcore-unbundled .PHONY: droidcore-unbundled droidcore-unbundled: $(info [2/4] including distdir.mk ...) include build/make/packaging/distdir.mk $(info [3/4] defining phony modules ...) include $(OUT_DIR)/soong/soong_phony_targets.mk goals := $(sort $(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS),$(call word-colon,1,$(pair)))) $(foreach goal,$(goals), \ $(eval .PHONY: $$(goal)) \ $(eval $$(goal):) \ $(if $(call streq,$(DIST),true),\ $(eval $$(goal): _dist_$$(goal)))) $(info [4/4] writing packaging rules ...) ================================================ FILE: rbesetup.sh ================================================ function _source_env_setup_script() { local -r ENV_SETUP_SCRIPT="build/make/envsetup.sh" local -r TOP_DIR=$( while [[ ! -f "${ENV_SETUP_SCRIPT}" ]] && [[ "${PWD}" != "/" ]]; do \cd .. done if [[ -f "${ENV_SETUP_SCRIPT}" ]]; then echo "$(PWD= /bin/pwd -P)" fi ) local -r FULL_PATH_ENV_SETUP_SCRIPT="${TOP_DIR}/${ENV_SETUP_SCRIPT}" if [[ ! -f "${FULL_PATH_ENV_SETUP_SCRIPT}" ]]; then echo "ERROR: Unable to source ${ENV_SETUP_SCRIPT}" return 1 fi # Need to change directory to the repo root so vendor scripts can be sourced # as well. local -r CUR_DIR=$PWD \cd "${TOP_DIR}" source "${FULL_PATH_ENV_SETUP_SCRIPT}" \cd "${CUR_DIR}" } # This function needs to run first as the remaining defining functions may be # using the envsetup.sh defined functions. Skip this part if this script is already # being invoked from envsetup.sh. if [[ "$1" != "--skip-envsetup" ]]; then _source_env_setup_script || return fi # This function prefixes the given command with appropriate variables needed # for the build to be executed with RBE. function use_rbe() { local RBE_BINARIES_DIR="prebuilts/remoteexecution-client/latest" local DOCKER_IMAGE="gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:1eb7f64b9e17102b970bd7a1af7daaebdb01c3fb777715899ef462d6c6d01a45" # Do not set an invocation-ID and let reproxy auto-generate one. USE_RBE="true" \ FLAG_exec_root="$(gettop)" \ FLAG_platform="container-image=docker://${DOCKER_IMAGE}" \ RBE_use_application_default_credentials="true" \ RBE_reproxy_wait_seconds="20" \ RBE_CXX_EXEC_STRATEGY="remote_local_fallback" \ RBE_cpp_dependency_scanner_plugin="${RBE_BINARIES_DIR}/dependency_scanner_go_plugin.so" \ RBE_DIR=${RBE_BINARIES_DIR} \ RBE_re_proxy="${RBE_BINARIES_DIR}/reproxy" \ $@ } # This function detects if the uploader is available and sets the path of it to # ANDROID_ENABLE_METRICS_UPLOAD. function _export_metrics_uploader() { local uploader_path="$(gettop)/vendor/google/misc/metrics_uploader_prebuilt/metrics_uploader.sh" if [[ -x "${uploader_path}" ]]; then export ANDROID_ENABLE_METRICS_UPLOAD="${uploader_path}" fi } # This function sets RBE specific environment variables needed for the build to # executed by RBE. This file should be sourced once per checkout of Android code. function _set_rbe_vars() { unset USE_GOMA export USE_RBE="true" export RBE_CXX_EXEC_STRATEGY="racing" export RBE_JAVAC_EXEC_STRATEGY="racing" export RBE_R8_EXEC_STRATEGY="racing" export RBE_D8_EXEC_STRATEGY="racing" export RBE_use_unified_cas_ops="true" export RBE_JAVAC=1 export RBE_R8=1 export RBE_D8=1 } _export_metrics_uploader _set_rbe_vars ================================================ FILE: shell_utils.sh ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. function gettop { local TOPFILE=build/make/core/envsetup.mk # The ${TOP-} expansion allows this to work even with set -u if [ -n "${TOP:-}" -a -f "${TOP:-}/$TOPFILE" ] ; then # The following circumlocution ensures we remove symlinks from TOP. (cd "$TOP"; PWD= /bin/pwd) else if [ -f $TOPFILE ] ; then # The following circumlocution (repeated below as well) ensures # that we record the true directory name and not one that is # faked up with symlink names. PWD= /bin/pwd else local HERE=$PWD local T= while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do \cd .. T=`PWD= /bin/pwd -P` done \cd "$HERE" if [ -f "$T/$TOPFILE" ]; then echo "$T" fi fi fi } # Asserts that the root of the tree can be found. if [ -z "${IMPORTING_ENVSETUP:-}" ] ; then function require_top { TOP=$(gettop) if [[ ! $TOP ]] ; then echo "Can not locate root of source tree. $(basename $0) must be run from within the Android source tree or TOP must be set." >&2 exit 1 fi } fi # Asserts that the lunch variables have been set if [ -z "${IMPORTING_ENVSETUP:-}" ] ; then function require_lunch { if [[ ! $TARGET_PRODUCT || ! $TARGET_RELEASE || ! $TARGET_BUILD_VARIANT ]] ; then echo "Please run lunch and try again." >&2 exit 1 fi } fi # This function sets up the build environment to be appropriate for Cog. function setup_cog_env_if_needed() { local top=$(gettop) # return early if not in a cog workspace if [[ ! "$top" =~ ^/google/cog ]]; then return 0 fi setup_cog_symlink export ANDROID_BUILD_ENVIRONMENT_CONFIG="googler-cog" # Running repo command within Cog workspaces is not supported, so override # it with this function. If the user is running repo within a Cog workspace, # we'll fail with an error, otherwise, we run the original repo command with # the given args. if ! ORIG_REPO_PATH=`which repo`; then return 0 fi function repo { if [[ "${PWD}" == /google/cog/* ]]; then echo -e "\e[01;31mERROR:\e[0mrepo command is disallowed within Cog workspaces." kill -INT $$ # exits the script without exiting the user's shell fi ${ORIG_REPO_PATH} "$@" } } # creates a symlink for the out/ dir when inside a cog workspace. function setup_cog_symlink() { local out_dir=$(getoutdir) local top=$(gettop) # return early if out dir is already a symlink. if [[ -L "$out_dir" ]]; then destination=$(readlink "$out_dir") # ensure the destination exists. mkdir -p "$destination" return 0 fi # return early if out dir is not in the workspace if [[ ! "$out_dir" =~ ^$top/ ]]; then return 0 fi local link_destination="${HOME}/.cog/android-build-out" # remove existing out/ dir if it exists if [[ -d "$out_dir" ]]; then echo "Detected existing out/ directory in the Cog workspace which is not supported. Repairing workspace by removing it and creating the symlink to ~/.cog/android-build-out" if ! rm -rf "$out_dir"; then echo "Failed to remove existing out/ directory: $out_dir" >&2 kill -INT $$ # exits the script without exiting the user's shell fi fi # create symlink echo "Creating symlink: $out_dir -> $link_destination" mkdir -p ${link_destination} if ! ln -s "$link_destination" "$out_dir"; then echo "Failed to create cog symlink: $out_dir -> $link_destination" >&2 kill -INT $$ # exits the script without exiting the user's shell fi } function getoutdir { local top=$(gettop) local out_dir="${OUT_DIR:-}" if [[ -z "${out_dir}" ]]; then if [[ -n "${OUT_DIR_COMMON_BASE:-}" && -n "${top}" ]]; then out_dir="${OUT_DIR_COMMON_BASE}/$(basename ${top})" else out_dir="out" fi fi if [[ "${out_dir}" != /* ]]; then out_dir="${top}/${out_dir}" fi echo "${out_dir}" } # Pretty print the build status and duration function _wrap_build() { if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then "$@" return $? fi local start_time=$(date +"%s") "$@" local ret=$? local end_time=$(date +"%s") local tdiff=$(($end_time-$start_time)) local hours=$(($tdiff / 3600 )) local mins=$((($tdiff % 3600) / 60)) local secs=$(($tdiff % 60)) local ncolors=$(tput colors 2>/dev/null) if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then color_failed=$'\E'"[0;31m" color_success=$'\E'"[0;32m" color_warning=$'\E'"[0;33m" color_reset=$'\E'"[00m" else color_failed="" color_success="" color_reset="" fi echo if [ $ret -eq 0 ] ; then echo -n "${color_success}#### build completed successfully " else echo -n "${color_failed}#### failed to build some targets " fi if [ $hours -gt 0 ] ; then printf "(%02d:%02d:%02d (hh:mm:ss))" $hours $mins $secs elif [ $mins -gt 0 ] ; then printf "(%02d:%02d (mm:ss))" $mins $secs elif [ $secs -gt 0 ] ; then printf "(%d seconds)" $secs fi echo " ####${color_reset}" echo return $ret } function log_tool_invocation() { if [[ -z $ANDROID_TOOL_LOGGER ]]; then return fi LOG_TOOL_TAG=$1 LOG_START_TIME=$(date +%s.%N) trap ' exit_code=$?; # Remove the trap to prevent duplicate log. trap - EXIT; $ANDROID_TOOL_LOGGER \ --tool_tag="${LOG_TOOL_TAG}" \ --start_timestamp="${LOG_START_TIME}" \ --end_timestamp="$(date +%s.%N)" \ --tool_args="$*" \ --exit_code="${exit_code}" \ ${ANDROID_TOOL_LOGGER_EXTRA_ARGS} \ > /dev/null 2>&1 & exit ${exit_code} ' SIGINT SIGTERM SIGQUIT EXIT } # Import the build variables supplied as arguments into this shell's environment. # For absolute variables, prefix the variable name with a '/'. For example: # import_build_vars OUT_DIR DIST_DIR /HOST_OUT_EXECUTABLES # Returns nonzero if the build command failed. Stderr is passed through. function import_build_vars() { require_top local script script=$(cd $TOP && build/soong/bin/get_build_vars "$@") local ret=$? if [ $ret -ne 0 ] ; then return $ret fi eval "$script" return $? } ================================================ FILE: tapasHelp.sh ================================================ #!/bin/bash # locate some directories cd "$(dirname $0)" SCRIPT_DIR="${PWD}" cd ../.. TOP="${PWD}" message='usage: tapas [ ...] [arm|x86|arm64|x86_64] [eng|userdebug|user] [devkeys] tapas selects individual apps to be built by the Android build system. Unlike "lunch", "tapas" does not request the building of images for a device. Additionally, an app built with "tapas" will have its dex file inside its apk, which should cause it to be suitable for installing on any api-compatible device. In other words, "tapas" configures the build of unbundled apps. The names ... should match LOCAL_PACKAGE_NAME as defined in an Android.mk The usage of the other arguments matches that of the rest of the platform build system and can be found by running `m help`' echo "$message" ================================================ FILE: target/board/BoardConfigGsiCommon.mk ================================================ # BoardConfigGsiCommon.mk # # Common compile-time definitions for GSI # Builds upon the mainline config. # # See device/generic/common/README.md for more details. # include build/make/target/board/BoardConfigMainlineCommon.mk TARGET_NO_KERNEL := true # This flag is set by mainline but isn't desired for GSI. BOARD_USES_SYSTEM_OTHER_ODEX := # system.img is ext4/erofs and non-sparsed. GSI_FILE_SYSTEM_TYPE ?= ext4 BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(GSI_FILE_SYSTEM_TYPE) TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true TARGET_USERIMAGES_SPARSE_EROFS_DISABLED := true # Enable system_dlkm image for creating a symlink in GSI to support # the devices with system_dlkm partition BOARD_USES_SYSTEM_DLKMIMAGE := true BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4 TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm # GSI also includes make_f2fs to support userdata parition in f2fs # for some devices TARGET_USERIMAGES_USE_F2FS := true # Enable dynamic system image size and reserved 64MB in it. BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE := 67108864 # GSI forces product and system_ext packages to /system for now. TARGET_COPY_OUT_PRODUCT := system/product TARGET_COPY_OUT_SYSTEM_EXT := system/system_ext BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := # Creates metadata partition mount point under root for # the devices with metadata parition BOARD_USES_METADATA_PARTITION := true # Android Verified Boot (AVB): # Set the rollback index to zero, to prevent the device bootloader from # updating the last seen rollback index in the tamper-evident storage. BOARD_AVB_ROLLBACK_INDEX := 0 # The chained vbmeta settings for boot images. BOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096 BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2 # Enable AVB chained partition for system. # https://android.googlesource.com/platform/external/avb/+/master/README.md BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 # Using sha256 for dm-verity partitions. b/156162446 BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 ifdef BUILDING_GSI # super.img spec for GSI targets BOARD_SUPER_PARTITION_SIZE := 3229614080 BOARD_SUPER_PARTITION_GROUPS := gsi_dynamic_partitions BOARD_GSI_DYNAMIC_PARTITIONS_PARTITION_LIST := system BOARD_GSI_DYNAMIC_PARTITIONS_SIZE := 3221225472 # Build pvmfw with GSI: b/376363989 ifeq (true,$(PRODUCT_BUILD_PVMFW_IMAGE)) BOARD_PVMFWIMAGE_PARTITION_SIZE := 0x00100000 endif endif # TODO(b/123695868, b/146149698): # This flag is set by mainline but isn't desired for GSI BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := # GSI specific System Properties ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext.prop else TARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext_user.prop endif # Set this to create /cache mount point for non-A/B devices that mounts /cache. # The partition size doesn't matter, just to make build pass. BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216 # Setup a vendor image to let PRODUCT_VENDOR_PROPERTIES does not affect GSI BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 ================================================ FILE: target/board/BoardConfigMainlineCommon.mk ================================================ # BoardConfigMainlineCommon.mk # # Common compile-time definitions for mainline images. # Ensure all trunk-stable flags are available. include build/make/target/product/build_variables.mk # The generic product target doesn't have any hardware-specific pieces. TARGET_NO_BOOTLOADER := true TARGET_NO_RECOVERY := true BOARD_EXT4_SHARE_DUP_BLOCKS := true TARGET_USERIMAGES_USE_EXT4 := true # Mainline devices must have /system_ext, /vendor and /product partitions. TARGET_COPY_OUT_SYSTEM_EXT := system_ext TARGET_COPY_OUT_VENDOR := vendor TARGET_COPY_OUT_PRODUCT := product BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 # Creates metadata partition mount point under root for # the devices with metadata parition BOARD_USES_METADATA_PARTITION := true # 64 bit mediadrmserver TARGET_ENABLE_MEDIADRM_64 := true # Puts odex files on system_other, as well as causing dex files not to get # stripped from APKs. BOARD_USES_SYSTEM_OTHER_ODEX := true # Audio: must using XML format for Treblized devices USE_XML_AUDIO_POLICY_CONF := 1 # Bluetooth defines # TODO(b/123695868): Remove the need for this BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := build/make/target/board/mainline_arm64/bluetooth BOARD_AVB_ENABLE := true BOARD_AVB_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_CHARGER_ENABLE_SUSPEND := true # Enable system property split for Treble BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true # Include stats logging code in LMKD TARGET_LMKD_STATS_LOG := true ================================================ FILE: target/board/BoardConfigPixelCommon.mk ================================================ # BoardConfigPixelCommon.mk # # Common compile-time definitions for Pixel devices. # Using sha256 for dm-verity partitions. b/156162446 # system, system_other, system_ext and product. BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_SYSTEM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_SYSTEM_EXT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 # vendor and odm. BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 # vendor_dlkm and odm_dlkm. BOARD_AVB_VENDOR_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 BOARD_AVB_ODM_DLKM_ADD_HASHTREE_FOOTER_ARGS += --hash_algorithm sha256 ================================================ FILE: target/board/android-info.mk ================================================ # # Set up product-global definitions and include product-specific rules. # LOCAL_PATH := $(call my-dir) -include $(TARGET_DEVICE_DIR)/AndroidBoard.mk # Generate a file that contains various information about the # device we're building for. This file is typically packaged up # with everything else. # # The following logic is used to find the contents of the info file: # 1. TARGET_BOARD_INFO_FILES (can be set in BoardConfig.mk) will be combined. # 2. TARGET_BOARD_INFO_FILE (can be set in BoardConfig.mk) will be used. # 3. $(TARGET_DEVICE_DIR)/board-info.txt will be used if present. # # Specifying both TARGET_BOARD_INFO_FILES and TARGET_BOARD_INFO_FILE is an # error. # INSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt ifdef TARGET_BOARD_INFO_FILES ifdef TARGET_BOARD_INFO_FILE $(warning Both TARGET_BOARD_INFO_FILES and TARGET_BOARD_INFO_FILE are defined.) $(warning Using $(TARGET_BOARD_INFO_FILES) rather than $(TARGET_BOARD_INFO_FILE) for android-info.txt) endif board_info_txt := $(call intermediates-dir-for,PACKAGING,board-info)/board-info.txt $(board_info_txt): $(TARGET_BOARD_INFO_FILES) $(hide) cat $(TARGET_BOARD_INFO_FILES) > $@ else ifdef TARGET_BOARD_INFO_FILE board_info_txt := $(TARGET_BOARD_INFO_FILE) else board_info_txt := $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt) endif CHECK_RADIO_VERSIONS := $(HOST_OUT_EXECUTABLES)/check_radio_versions$(HOST_EXECUTABLE_SUFFIX) $(INSTALLED_ANDROID_INFO_TXT_TARGET): $(board_info_txt) $(CHECK_RADIO_VERSIONS) $(hide) $(CHECK_RADIO_VERSIONS) \ --board_info_txt $(board_info_txt) \ --board_info_check $(BOARD_INFO_CHECK) $(call pretty,"Generated: ($@)") ifdef board_info_txt $(hide) grep -v '#' $< > $@ else ifdef TARGET_BOOTLOADER_BOARD_NAME $(hide) echo "board=$(TARGET_BOOTLOADER_BOARD_NAME)" > $@ else $(hide) echo "" > $@ endif $(call declare-0p-target,$(INSTALLED_ANDROID_INFO_TXT_TARGET)) # Copy compatibility metadata to the device. # DEVICE_MANIFEST_SKUS: a list of SKUS where DEVICE_MANIFEST__FILES is defined. ifdef DEVICE_MANIFEST_SKUS # Install /vendor/etc/vintf/manifest_$(sku).xml # $(1): sku define _add_device_sku_manifest my_fragment_files_var := DEVICE_MANIFEST_$$(call to-upper,$(1))_FILES ifndef $$(my_fragment_files_var) $$(error $(1) is in DEVICE_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined) endif my_fragment_files := $$($$(my_fragment_files_var)) include $$(CLEAR_VARS) LOCAL_MODULE := vendor_manifest_$(1).xml LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice LOCAL_MODULE_STEM := manifest_$(1).xml LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/vintf GEN := $$(local-generated-sources-dir)/manifest_$(1).xml $$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files) $$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf BOARD_SEPOLICY_VERS=$$(BOARD_SEPOLICY_VERS) \ PRODUCT_ENFORCE_VINTF_MANIFEST=$$(PRODUCT_ENFORCE_VINTF_MANIFEST) \ $$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \ -i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES)) LOCAL_PREBUILT_MODULE_FILE := $$(GEN) include $$(BUILD_PREBUILT) my_fragment_files_var := my_fragment_files := endef $(foreach sku, $(DEVICE_MANIFEST_SKUS), $(eval $(call _add_device_sku_manifest,$(sku)))) _add_device_sku_manifest := endif # DEVICE_MANIFEST_SKUS # ODM_MANIFEST_SKUS: a list of SKUS where ODM_MANIFEST__FILES are defined. ifdef ODM_MANIFEST_SKUS # Install /odm/etc/vintf/manifest_$(sku).xml # $(1): sku define _add_odm_sku_manifest my_fragment_files_var := ODM_MANIFEST_$$(call to-upper,$(1))_FILES ifndef $$(my_fragment_files_var) $$(error $(1) is in ODM_MANIFEST_SKUS but $$(my_fragment_files_var) is not defined) endif my_fragment_files := $$($$(my_fragment_files_var)) include $$(CLEAR_VARS) LOCAL_MODULE := odm_manifest_$(1).xml LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice LOCAL_MODULE_STEM := manifest_$(1).xml LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_RELATIVE_PATH := vintf LOCAL_ODM_MODULE := true GEN := $$(local-generated-sources-dir)/manifest_$(1).xml $$(GEN): PRIVATE_SRC_FILES := $$(my_fragment_files) $$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf VINTF_IGNORE_TARGET_FCM_VERSION=true \ $$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \ -i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES)) LOCAL_PREBUILT_MODULE_FILE := $$(GEN) include $$(BUILD_PREBUILT) my_fragment_files_var := my_fragment_files := endef $(foreach sku, $(ODM_MANIFEST_SKUS), $(eval $(call _add_odm_sku_manifest,$(sku)))) _add_odm_sku_manifest := endif # ODM_MANIFEST_SKUS ================================================ FILE: target/board/generic/AndroidBoard.mk ================================================ LOCAL_PATH := $(call my-dir) ================================================ FILE: target/board/generic/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # arm emulator specific definitions TARGET_ARCH := arm # Note: Before P, we built the platform images for ARMv7-A _without_ NEON. # Note: Before Q, we built the CTS and SDK images for ARMv7-A _without_ NEON. # Note: Before Q, we built unbundled apps for ARMv7-A _without_ NEON. # # Starting from Pi, System image of aosp_arm products is the new GSI # for real devices newly launched for Pi. These devices are usualy not # as performant as the mainstream 64-bit devices and the performance # provided by NEON is important for them to pass related CTS tests. TARGET_ARCH_VARIANT := armv7-a-neon TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi include build/make/target/board/BoardConfigGsiCommon.mk ================================================ FILE: target/board/generic/README.txt ================================================ The "generic" product defines a non-hardware-specific target without a kernel or bootloader. It can be used to build the entire user-level system, and will work with the emulator, though sound will not work (see the "emulator" product for that). It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic/device.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic/system_ext.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib/libreference-ril.so ================================================ FILE: target/board/generic_64bitonly_x86_64/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # x86_64 emulator specific definitions TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 # Keep the following for 32-bit native code support # There are a few native services still on 32-bit modes, e.g. media & audio. # Remove them in S. TARGET_2ND_CPU_ABI := x86 TARGET_2ND_ARCH := x86 TARGET_2ND_ARCH_VARIANT := x86_64 TARGET_PRELINK_MODULE := false include build/make/target/board/BoardConfigGsiCommon.mk ================================================ FILE: target/board/generic_64bitonly_x86_64/README.txt ================================================ The "generic_x86_64_app" product defines a non-hardware-specific IA target without a kernel or bootloader. It can be used to build the entire user-level system, and will work with the IA version of the emulator, This supports 64-bit apps only. ================================================ FILE: target/board/generic_64bitonly_x86_64/device.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ifdef NET_ETH0_STARTONBOOT PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1 endif ================================================ FILE: target/board/generic_64bitonly_x86_64/system.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib64/libreference-ril.so ================================================ FILE: target/board/generic_arm64/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # arm64 emulator specific definitions TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := arm64-v8a TARGET_2ND_ARCH := arm TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi ifneq ($(TARGET_BUILD_APPS)$(filter sdk,$(MAKECMDGOALS)),) # DO NOT USE # DO NOT USE # # This architecture / CPU variant must NOT be used for any 64 bit # platform builds. It is the lowest common denominator required # to build an unbundled application or cts for all supported 32 and 64 bit # platforms. It now recommended to use generic_arm64_plus_armv7 to achieve this. # # If you're building a 64 bit platform (and not an application) the # ARM-v8 specification allows you to assume all the features available in an # armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant: # # TARGET_2ND_ARCH_VARIANT := armv8-a # TARGET_2ND_CPU_VARIANT := generic # # DO NOT USE # DO NOT USE TARGET_2ND_ARCH_VARIANT := armv7-a-neon # DO NOT USE # DO NOT USE TARGET_2ND_CPU_VARIANT := generic # DO NOT USE # DO NOT USE else TARGET_2ND_ARCH_VARIANT := armv8-a TARGET_2ND_CPU_VARIANT := generic endif # Include 64-bit mediaserver to support 64-bit only devices TARGET_DYNAMIC_64_32_MEDIASERVER := true # Include 64-bit drmserver to support 64-bit only devices TARGET_DYNAMIC_64_32_DRMSERVER := true include build/make/target/board/BoardConfigGsiCommon.mk # Some vendors still haven't cleaned up all device specific directories under # root! # TODO(b/111434759, b/111287060) SoC specific hacks BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware # for Android.bp TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS := true # TODO(b/36764215): remove this setting when the generic system image # no longer has QCOM-specific directories under /. BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy ================================================ FILE: target/board/generic_arm64/README.txt ================================================ The "generic_arm64" product defines a non-hardware-specific arm64 target without a bootloader. It is also the target to build the generic kernel image (GKI). It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic_arm64/device.mk ================================================ # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_arm64/sepolicy/OWNERS ================================================ include platform/system/sepolicy:/OWNERS ================================================ FILE: target/board/generic_arm64/sepolicy/file.te ================================================ # TODO(b/36764215): remove this file when the generic system image # no longer has these directories type persist_file, file_type; # Default type for anything under /firmware. type firmware_file, fs_type, contextmount_type; ================================================ FILE: target/board/generic_arm64/sepolicy/file_contexts ================================================ # TODO(b/36764215): remove this file when the generic system image # no longer has these directories. They are specific to QCOM. # / /tombstones u:object_r:rootfs:s0 /dsp u:object_r:rootfs:s0 # /persist /persist(/.*)? u:object_r:persist_file:s0 # files in firmware /firmware(/.*)? u:object_r:firmware_file:s0 ================================================ FILE: target/board/generic_arm64/system_ext.prop ================================================ # # system.prop for generic arm64 sdk # rild.libpath=/vendor/lib64/libreference-ril.so ================================================ FILE: target/board/generic_arm64_plus_armv7/BoardConfig.mk ================================================ # Copyright (C) 2025 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # arm64 emulator specific definitions TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := arm64-v8a TARGET_2ND_ARCH := arm TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi # DO NOT USE # DO NOT USE # # This architecture / CPU variant must NOT be used for any 64 bit # platform builds. It is the lowest common denominator required # to build an unbundled application or cts for all supported 32 and 64 bit # platforms. # # If you're building a 64 bit platform (and not an application) the # ARM-v8 specification allows you to assume all the features available in an # armv7-a-neon CPU. You should set the following as 2nd arch/cpu variant: # # TARGET_2ND_ARCH_VARIANT := armv8-a # TARGET_2ND_CPU_VARIANT := generic # # DO NOT USE # DO NOT USE TARGET_2ND_ARCH_VARIANT := armv7-a-neon # DO NOT USE # DO NOT USE TARGET_2ND_CPU_VARIANT := generic # DO NOT USE # DO NOT USE # Include 64-bit mediaserver to support 64-bit only devices TARGET_DYNAMIC_64_32_MEDIASERVER := true # Include 64-bit drmserver to support 64-bit only devices TARGET_DYNAMIC_64_32_DRMSERVER := true include build/make/target/board/BoardConfigGsiCommon.mk ================================================ FILE: target/board/generic_arm64_plus_armv7/README.txt ================================================ The "generic_arm64_plus_armv7" product defines a non-hardware-specific arm64 target with armv7 compatible arm32. It is used for building CTS and other test suites for which the 32-bit binaries may be run on older devices with armv7 CPUs. It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic_arm64_plus_armv7/device.mk ================================================ # # Copyright (C) 2025 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_riscv64/BoardConfig.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # riscv64 emulator specific definitions TARGET_ARCH := riscv64 TARGET_ARCH_VARIANT := TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := riscv64 # Include 64-bit mediaserver to support 64-bit only devices TARGET_DYNAMIC_64_32_MEDIASERVER := true include build/make/target/board/BoardConfigGsiCommon.mk # Temporary hack while prebuilt modules are missing riscv64. ALLOW_MISSING_DEPENDENCIES := true ================================================ FILE: target/board/generic_riscv64/README.txt ================================================ The "generic_riscv64" product defines a non-hardware-specific riscv64 target without a bootloader. It is also the target to build the generic kernel image (GKI). It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic_riscv64/device.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_riscv64/system_ext.prop ================================================ # # system.prop for generic riscv64 sdk # rild.libpath=/vendor/lib64/libreference-ril.so ================================================ FILE: target/board/generic_x86/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # x86 emulator specific definitions TARGET_CPU_ABI := x86 TARGET_ARCH := x86 TARGET_ARCH_VARIANT := x86 include build/make/target/board/BoardConfigGsiCommon.mk ================================================ FILE: target/board/generic_x86/README.txt ================================================ The "generic_x86" product defines a non-hardware-specific IA target without a kernel or bootloader. It can be used to build the entire user-level system, and will work with the IA version of the emulator, It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic_x86/device.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ifdef NET_ETH0_STARTONBOOT PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 endif ================================================ FILE: target/board/generic_x86/system_ext.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib/libreference-ril.so ================================================ FILE: target/board/generic_x86_64/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # x86_64 emulator specific definitions TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 TARGET_2ND_CPU_ABI := x86 TARGET_2ND_ARCH := x86 TARGET_2ND_ARCH_VARIANT := x86_64 # Include 64-bit mediaserver to support 64-bit only devices TARGET_DYNAMIC_64_32_MEDIASERVER := true # Include 64-bit drmserver to support 64-bit only devices TARGET_DYNAMIC_64_32_DRMSERVER := true include build/make/target/board/BoardConfigGsiCommon.mk ================================================ FILE: target/board/generic_x86_64/README.txt ================================================ The "generic_x86_64" product defines a non-hardware-specific x86_64 target without a bootloader. It is also the target to build the generic kernel image (GKI). It is not a product "base class"; no other products inherit from it or use it in any way. ================================================ FILE: target/board/generic_x86_64/device.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_x86_64/system_ext.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib64/libreference-ril.so ================================================ FILE: target/board/generic_x86_64_arm64/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 TARGET_2ND_CPU_ABI := x86 TARGET_2ND_ARCH := x86 TARGET_2ND_ARCH_VARIANT := x86_64 TARGET_NATIVE_BRIDGE_ARCH := arm64 TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv8-a TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic TARGET_NATIVE_BRIDGE_ABI := arm64-v8a TARGET_NATIVE_BRIDGE_2ND_ARCH := arm TARGET_NATIVE_BRIDGE_2ND_ARCH_VARIANT := armv7-a-neon TARGET_NATIVE_BRIDGE_2ND_CPU_VARIANT := generic TARGET_NATIVE_BRIDGE_2ND_ABI := armeabi-v7a armeabi BUILD_BROKEN_DUP_RULES := true TARGET_PRELINK_MODULE := false include build/make/target/board/BoardConfigMainlineCommon.mk # the settings differ from BoardConfigMainlineCommon.mk BOARD_USES_SYSTEM_OTHER_ODEX := # Resize to 4G to accommodate ASAN and CTS BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 ================================================ FILE: target/board/generic_x86_64_arm64/README.txt ================================================ The "generic_x86_64" product defines a non-hardware-specific IA target without a kernel or bootloader. It can be used to build the entire user-level system, and will work with the IA version of the emulator, It is not a product "base class"; no other products inherit from it or use it in any way. Third party arm64 to x86_64 translator has to be installed as well ================================================ FILE: target/board/generic_x86_64_arm64/device.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_x86_64_arm64/system_ext.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib64/libreference-ril.so ================================================ FILE: target/board/generic_x86_arm/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_CPU_ABI := x86 TARGET_ARCH := x86 TARGET_ARCH_VARIANT := x86 TARGET_NATIVE_BRIDGE_ARCH := arm TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi BUILD_BROKEN_DUP_RULES := true # # The inclusion order below is important. # The settings in latter makefiles overwrite those in the former. # include build/make/target/board/BoardConfigMainlineCommon.mk # the settings differ from BoardConfigMainlineCommon.mk BOARD_USES_SYSTEM_OTHER_ODEX := # Resize to 4G to accomodate ASAN and CTS BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296 ================================================ FILE: target/board/generic_x86_arm/README.txt ================================================ The "generic_x86_arm" product defines a non-hardware-specific IA target without a kernel or bootloader. It can be used to build the entire user-level system, and will work with the IA version of the emulator, It is not a product "base class"; no other products inherit from it or use it in any way. Third party arm to x86 translator has to be installed as well ================================================ FILE: target/board/generic_x86_arm/device.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/generic_x86_arm/system_ext.prop ================================================ # # system.prop for generic sdk # rild.libpath=/vendor/lib/libreference-ril.so ================================================ FILE: target/board/go_defaults.prop ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ================================================ FILE: target/board/go_defaults_512.prop ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # 512MB specific properties. # lmkd can kill more now. ro.lmk.medium=700 # madvise random in ART to reduce page cache thrashing. dalvik.vm.madvise-random=true ================================================ FILE: target/board/go_defaults_common.prop ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Sets Android Go recommended default values for propreties. # Set lowram options ro.lmk.critical_upgrade=true ro.lmk.upgrade_pressure=40 ro.lmk.downgrade_pressure=60 ro.lmk.kill_heaviest_task=false # set threshold to filter unused apps pm.dexopt.downgrade_after_inactive_days=10 # set the compiler filter for shared apks to quicken. # Rationale: speed has a lot of dex code expansion, it uses more ram and space # compared to quicken. Using quicken for shared APKs on Go devices may save RAM. # Note that this is a trade-off: here we trade clean pages for dirty pages, # extra cpu and battery. That's because the quicken files will be jit-ed in all # the processes that load of shared apk and the code cache is not shared. # Some notable apps that will be affected by this are gms and chrome. # b/65591595. pm.dexopt.shared=quicken # Default heap sizes. Allow up to 256m for large heaps to make sure a single app # doesn't take all of the RAM. dalvik.vm.heapgrowthlimit=128m dalvik.vm.heapsize=256m ================================================ FILE: target/board/gsi_arm64/BoardConfig.mk ================================================ # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include build/make/target/board/BoardConfigGsiCommon.mk TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_ABI := arm64-v8a TARGET_CPU_ABI2 := TARGET_CPU_VARIANT := generic TARGET_2ND_ARCH := arm TARGET_2ND_ARCH_VARIANT := armv8-a TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi TARGET_2ND_CPU_VARIANT := generic # Include 64-bit mediaserver to support 64-bit only devices TARGET_DYNAMIC_64_32_MEDIASERVER := true # Include 64-bit drmserver to support 64-bit only devices TARGET_DYNAMIC_64_32_DRMSERVER := true # TODO(b/111434759, b/111287060) SoC specific hacks BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt:/firmware # TODO(b/36764215): remove this setting when the generic system image # no longer has QCOM-specific directories under /. BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy ================================================ FILE: target/board/gsi_system_ext.prop ================================================ # GSI always generate dex pre-opt in system image ro.cp_system_other_odex=0 # GSI always disables adb authentication ro.adb.secure=0 # GSI disables non-AOSP nnapi extensions on product partition ro.nnapi.extensions.deny_on_product=true # TODO(b/120679683): disable RescueParty before all problem apps solved persist.sys.disable_rescue=true # TODO(b/78105955): disable privapp_permissions checking before the bug solved ro.control_privapp_permissions=disable ================================================ FILE: target/board/gsi_system_ext_user.prop ================================================ # GSI always generate dex pre-opt in system image ro.cp_system_other_odex=0 # GSI disables non-AOSP nnapi extensions on product partition ro.nnapi.extensions.deny_on_product=true # TODO(b/120679683): disable RescueParty before all problem apps solved persist.sys.disable_rescue=true # TODO(b/78105955): disable privapp_permissions checking before the bug solved ro.control_privapp_permissions=disable ================================================ FILE: target/board/linux_bionic/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This "device" is only intended to be used for host Bionic build targets, so # (device) target architectures are irrelevant. However, the build system isn't # prepared to handle no target architectures at all, so pick something # arbitrarily. TARGET_ARCH := arm TARGET_ARCH_VARIANT := armv7-a-neon TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi HOST_CROSS_OS := linux_bionic HOST_CROSS_ARCH := x86_64 HOST_CROSS_2ND_ARCH := ================================================ FILE: target/board/linux_bionic/README.md ================================================ This "device" is suitable for Soong-only builds to create Bionic binaries for Linux hosts: ``` build/soong/soong_ui.bash --make-mode --soong-only TARGET_PRODUCT=linux_bionic ... ``` ================================================ FILE: target/board/mainline_arm64/BoardConfig.mk ================================================ # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := arm64-v8a TARGET_2ND_ARCH := arm TARGET_2ND_ARCH_VARIANT := armv8-a TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi TARGET_2ND_CPU_VARIANT := generic include build/make/target/board/BoardConfigMainlineCommon.mk # TODO(b/143732851): Remove this after replacing /persit with # /mnt/vendor/persist BOARD_ROOT_EXTRA_SYMLINKS += /mnt/vendor/persist:/persist BOARD_SEPOLICY_DIRS += build/make/target/board/mainline_arm64/sepolicy TARGET_NO_KERNEL := true # Build generic A/B format system-only OTA. AB_OTA_UPDATER := true AB_OTA_PARTITIONS := system BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 ================================================ FILE: target/board/mainline_arm64/bluetooth/bdroid_buildcfg.h ================================================ /* * * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Not a Contribution, Apache license notifications and license are retained * for attribution purposes only. * * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _BDROID_BUILDCFG_H #define _BDROID_BUILDCFG_H // VSC spec support #define BLE_VND_INCLUDED TRUE #endif ================================================ FILE: target/board/mainline_arm64/sepolicy/OWNERS ================================================ include platform/system/sepolicy:/OWNERS ================================================ FILE: target/board/mainline_arm64/sepolicy/file.te ================================================ # TODO(b/143732851): remove this file when the mainline system image # no longer need these SoC specific directory type persist_file, file_type; ================================================ FILE: target/board/mainline_arm64/sepolicy/file_contexts ================================================ # TODO(b/143732851): remove this file when the mainline system image # no longer need these SoC specific directory # /persist /persist(/.*)? u:object_r:persist_file:s0 ================================================ FILE: target/board/mainline_sdk/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Ensure all trunk-stable flags are available. include build/make/target/product/build_variables.mk TARGET_ARCH_SUITE := mainline_sdk HOST_CROSS_OS := linux_bionic HOST_CROSS_ARCH := x86_64 HOST_CROSS_2ND_ARCH := ================================================ FILE: target/board/mainline_sdk/README.md ================================================ This device is suitable for a soong-only build that builds for all the architectures needed for mainline modules sdk prebuilts. ================================================ FILE: target/board/mainline_x86/BoardConfig.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := x86 TARGET_ARCH_VARIANT := x86 TARGET_CPU_ABI := x86 include build/make/target/board/BoardConfigMainlineCommon.mk TARGET_NO_KERNEL := true # Build generic A/B format system-only OTA. AB_OTA_UPDATER := true AB_OTA_PARTITIONS := system BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 ================================================ FILE: target/board/mainline_x86_64/BoardConfig.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 TARGET_CPU_ABI := x86_64 TARGET_2ND_ARCH := x86 TARGET_2ND_ARCH_VARIANT := x86_64 TARGET_2ND_CPU_ABI := x86 include build/make/target/board/BoardConfigMainlineCommon.mk TARGET_NO_KERNEL := true # Build generic A/B format system-only OTA. AB_OTA_UPDATER := true AB_OTA_PARTITIONS := system BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 ================================================ FILE: target/board/mainline_x86_arm/BoardConfig.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := x86 TARGET_ARCH_VARIANT := x86 TARGET_CPU_ABI := x86 TARGET_NATIVE_BRIDGE_ARCH := arm TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi include build/make/target/board/BoardConfigMainlineCommon.mk TARGET_NO_KERNEL := true # Build generic A/B format system-only OTA. AB_OTA_UPDATER := true AB_OTA_PARTITIONS := system BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 ================================================ FILE: target/board/module_arm/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := arm TARGET_ARCH_VARIANT := armv7-a-neon TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi ================================================ FILE: target/board/module_arm/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an arm device. ================================================ FILE: target/board/module_arm64/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := arm64-v8a TARGET_2ND_ARCH := arm TARGET_2ND_ARCH_VARIANT := armv8-a TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi TARGET_2ND_CPU_VARIANT := generic ================================================ FILE: target/board/module_arm64/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an arm64 device. 32 bit binaries built with this product will not be suitable for a 32-bit arm device. ================================================ FILE: target/board/module_arm64only/BoardConfig.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := arm64-v8a ================================================ FILE: target/board/module_arm64only/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an arm64 device. 32 bit binaries will not be built. ================================================ FILE: target/board/module_riscv64/BoardConfig.mk ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH := riscv64 TARGET_ARCH_VARIANT := TARGET_CPU_VARIANT := generic TARGET_CPU_ABI := riscv64 # Temporary hack while prebuilt modules are missing riscv64. ALLOW_MISSING_DEPENDENCIES := true ================================================ FILE: target/board/module_riscv64/README.md ================================================ This device is suitable for an unbundled module targeted specifically to a riscv64 device. This is a 64-bit only device (no 32-bit support). ================================================ FILE: target/board/module_x86/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_CPU_ABI := x86 TARGET_ARCH := x86 TARGET_ARCH_VARIANT := x86 ================================================ FILE: target/board/module_x86/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an x86 device. ================================================ FILE: target/board/module_x86_64/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 TARGET_2ND_CPU_ABI := x86 TARGET_2ND_ARCH := x86 TARGET_2ND_ARCH_VARIANT := x86_64 ================================================ FILE: target/board/module_x86_64/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an x86_64 device. 32 bit binaries built with this product will not be suitable for a 32-bit x86 device. ================================================ FILE: target/board/module_x86_64only/BoardConfig.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 ================================================ FILE: target/board/module_x86_64only/README.md ================================================ This device is suitable for an unbundled module targeted specifically to an x86_64 device. 32 bit binaries will not be built. ================================================ FILE: target/board/ndk/BoardConfig.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGET_ARCH_SUITE := ndk ================================================ FILE: target/board/ndk/README.md ================================================ This device is suitable for a soong-only build that builds for all the architectures needed for the ndk. ================================================ FILE: target/product/AndroidProducts.mk ================================================ # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # This file should set PRODUCT_MAKEFILES to a list of product makefiles # to expose to the build system. LOCAL_DIR will already be set to # the directory containing this file. # PRODUCT_MAKEFILES is set up in AndroidProducts.mks. # Format of PRODUCT_MAKEFILES: # : # If the is the same as the base file name (without dir # and the .mk suffix) of the product makefile, ":" can be # omitted. # # This file may not rely on the value of any variable other than # LOCAL_DIR; do not use any conditionals, and do not look up the # value of any variable that isn't set in this file or in a file that # it includes. # # Unbundled apps will be built with the most generic product config. ifneq ($(TARGET_BUILD_APPS),) PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/aosp_arm64.mk \ $(LOCAL_DIR)/aosp_arm64_fullmte.mk \ $(LOCAL_DIR)/aosp_arm64_plus_armv7.mk \ $(LOCAL_DIR)/aosp_arm.mk \ $(LOCAL_DIR)/aosp_riscv64.mk \ $(LOCAL_DIR)/aosp_x86_64.mk \ $(LOCAL_DIR)/aosp_x86.mk \ $(LOCAL_DIR)/full.mk \ $(LOCAL_DIR)/full_x86.mk \ else PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/aosp_64bitonly_x86_64.mk \ $(LOCAL_DIR)/aosp_arm64.mk \ $(LOCAL_DIR)/aosp_arm64_fullmte.mk \ $(LOCAL_DIR)/aosp_arm64_plus_armv7.mk \ $(LOCAL_DIR)/aosp_arm.mk \ $(LOCAL_DIR)/aosp_riscv64.mk \ $(LOCAL_DIR)/aosp_x86_64.mk \ $(LOCAL_DIR)/aosp_x86_arm.mk \ $(LOCAL_DIR)/aosp_x86.mk \ $(LOCAL_DIR)/full.mk \ $(LOCAL_DIR)/full_x86.mk \ $(LOCAL_DIR)/generic.mk \ $(LOCAL_DIR)/generic_system_arm64.mk \ $(LOCAL_DIR)/generic_system_x86.mk \ $(LOCAL_DIR)/generic_system_x86_64.mk \ $(LOCAL_DIR)/generic_system_x86_arm.mk \ $(LOCAL_DIR)/generic_x86.mk \ $(LOCAL_DIR)/mainline_system_arm64.mk \ $(LOCAL_DIR)/mainline_system_x86.mk \ $(LOCAL_DIR)/mainline_system_x86_64.mk \ $(LOCAL_DIR)/mainline_system_x86_arm.mk \ $(LOCAL_DIR)/ndk.mk \ $(LOCAL_DIR)/sdk.mk \ $(LOCAL_DIR)/sdk_with_runtime_apis.mk \ endif PRODUCT_MAKEFILES += \ $(LOCAL_DIR)/linux_bionic.mk \ $(LOCAL_DIR)/mainline_sdk.mk \ $(LOCAL_DIR)/module_arm.mk \ $(LOCAL_DIR)/module_arm64.mk \ $(LOCAL_DIR)/module_arm64only.mk \ $(LOCAL_DIR)/module_riscv64.mk \ $(LOCAL_DIR)/module_x86.mk \ $(LOCAL_DIR)/module_x86_64.mk \ $(LOCAL_DIR)/module_x86_64only.mk \ COMMON_LUNCH_CHOICES := \ aosp_arm64-trunk_staging-eng \ aosp_arm-trunk_staging-eng \ aosp_x86_64-trunk_staging-eng \ aosp_x86-trunk_staging-eng \ ================================================ FILE: target/product/OWNERS ================================================ per-file runtime_libart.mk = mast@google.com, ngeoffray@google.com, rpl@google.com, vmarko@google.com # GSI per-file gsi_release.mk = file:/target/product/gsi/OWNERS per-file developer_gsi_keys.mk = file:/target/product/gsi/OWNERS # Android Go per-file go_defaults.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com per-file go_defaults_512.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com per-file go_defaults_common.mk = gkaiser@google.com, kushg@google.com, rajekumar@google.com # Translation per-file languages_default.mk = aapple@google.com ================================================ FILE: target/product/angle_default.mk ================================================ # # Copyright 2023 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # To enable ANGLE as the default system GLES drivers, add # $(call inherit-product, $(SRC_TARGET_DIR)/product/angle_default.mk) to the Makefile. PRODUCT_SYSTEM_PROPERTIES += \ persist.graphics.egl=angle ================================================ FILE: target/product/aosp_64bitonly_x86_64.mk ================================================ # # Copyright 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # The system image of aosp_x86_64_app-userdebug is a GSI for the devices with: # - x86 64 bits user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. # GSI for system/product & support 64-bit apps only $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk) # Enable mainline checking for excat this product name ifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk) # # Special settings for GSI releasing # ifeq (aosp_64bitonly_x86_64,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) endif PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ root/init.zygote64.rc # This build configuration supports 64-bit apps only PRODUCT_NAME := aosp_64bitonly_x86_64 PRODUCT_DEVICE := generic_64bitonly_x86_64 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on x86_64 App ================================================ FILE: target/product/aosp_arm.mk ================================================ # # Copyright 2017 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # The system image of aosp_arm-userdebug is a GSI for the devices with: # - ARM 32 bits user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) # Enable mainline checking for excat this product name ifeq (aosp_arm,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk) $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) # # Special settings for GSI releasing # ifeq (aosp_arm,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image USE_SOONG_DEFINED_SYSTEM_IMAGE := true PRODUCT_USE_SOONG_NOTICE_XML := true endif PRODUCT_NAME := aosp_arm PRODUCT_DEVICE := generic PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on ARM32 ================================================ FILE: target/product/aosp_arm64.mk ================================================ # # Copyright (C) 2017 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # The system image of aosp_arm64-userdebug is a GSI for the devices with: # - ARM 64 bits user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) # Enable mainline checking for excat this product name ifeq (aosp_arm64,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # pKVM $(call inherit-product-if-exists, packages/modules/Virtualization/apex/product_packages.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor or vendor_boot image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk) AB_OTA_UPDATER := true AB_OTA_PARTITIONS ?= system # # Special settings for GSI releasing # ifeq (aosp_arm64,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image USE_SOONG_DEFINED_SYSTEM_IMAGE := true PRODUCT_USE_SOONG_NOTICE_XML := true endif PRODUCT_NAME := aosp_arm64 PRODUCT_DEVICE := generic_arm64 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on ARM64 PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true ================================================ FILE: target/product/aosp_arm64_fullmte.mk ================================================ # Copyright (C) 2023 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include $(SRC_TARGET_DIR)/product/fullmte.mk PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_arm64.mk) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_NAME := aosp_arm64_fullmte ================================================ FILE: target/product/aosp_arm64_plus_armv7.mk ================================================ # # Copyright (C) 2025 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # aosp_arm64_plus_armv7 is for building CTS and other test suites with # arm64 as the primary architecture and armv7 arm32 as the secondary # architecture. # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # pKVM $(call inherit-product-if-exists, packages/modules/Virtualization/apex/product_packages.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor or vendor_boot image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk) AB_OTA_UPDATER := true AB_OTA_PARTITIONS ?= system # # Special settings for GSI releasing # # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_NAME := aosp_arm64_plus_armv7 PRODUCT_DEVICE := generic_arm64_plus_armv7 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on ARM64 with ARMV7 PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true ================================================ FILE: target/product/aosp_base.mk ================================================ # # Copyright 2013 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk) ================================================ FILE: target/product/aosp_base_telephony.mk ================================================ # # Copyright 2013 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk) PRODUCT_PACKAGES += \ messaging ================================================ FILE: target/product/aosp_product.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Includes all AOSP product packages $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk) # Default AOSP sounds $(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk) # Additional settings used in all AOSP builds PRODUCT_PRODUCT_PROPERTIES += \ ro.config.ringtone?=Ring_Synth_04.ogg \ ro.config.notification_sound?=pixiedust.ogg \ ro.com.android.dataroaming?=true \ # More AOSP packages PRODUCT_PACKAGES += \ initial-package-stopped-states-aosp.xml \ messaging \ PhotoTable \ preinstalled-packages-platform-aosp-product.xml \ ThemePicker \ # Telephony: # Provide a APN configuration to GSI product PRODUCT_COPY_FILES += \ device/sample/etc/apns-full-conf.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/apns-conf.xml ================================================ FILE: target/product/aosp_riscv64.mk ================================================ # # Copyright 2022 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # The system image of aosp_riscv64-userdebug is a GSI for the devices with: # - riscv64 user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. # GSI for system/product & support 64-bit apps only $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk) # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_riscv64/device.mk) # # Special settings for GSI releasing # ifeq (aosp_riscv64,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) endif PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ root/init.zygote64.rc # TODO(b/206676167): This property can be removed when renderscript is removed. # Prevents framework from attempting to load renderscript libraries, which are # not supported on this architecture. PRODUCT_SYSTEM_PROPERTIES += \ config.disable_renderscript=1 \ # This build configuration supports 64-bit apps only PRODUCT_NAME := aosp_riscv64 PRODUCT_DEVICE := generic_riscv64 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on Riscv64 ================================================ FILE: target/product/aosp_x86.mk ================================================ # # Copyright 2013 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # The system image of aosp_x86-userdebug is a GSI for the devices with: # - x86 32 bits user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) # Enable mainline checking for excat this product name ifeq (aosp_x86,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) # # Special settings for GSI releasing # ifeq (aosp_x86,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image USE_SOONG_DEFINED_SYSTEM_IMAGE := true PRODUCT_USE_SOONG_NOTICE_XML := true endif PRODUCT_NAME := aosp_x86 PRODUCT_DEVICE := generic_x86 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on x86 ================================================ FILE: target/product/aosp_x86_64.mk ================================================ # # Copyright 2013 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # The system image of aosp_x86_64-userdebug is a GSI for the devices with: # - x86 64 bits user space # - 64 bits binder interface # - system-as-root # - VNDK enforcement # - compatible property override enabled # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) # Enable mainline checking for excat this product name ifeq (aosp_x86_64,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # pKVM $(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk) AB_OTA_UPDATER := true AB_OTA_PARTITIONS ?= system # # Special settings for GSI releasing # ifeq (aosp_x86_64,$(TARGET_PRODUCT)) # Build modules from source if this has not been pre-configured MODULE_BUILD_FROM_SOURCE ?= true $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk) PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image USE_SOONG_DEFINED_SYSTEM_IMAGE := true PRODUCT_USE_SOONG_NOTICE_XML := true endif PRODUCT_NAME := aosp_x86_64 PRODUCT_DEVICE := generic_x86_64 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on x86_64 PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true ================================================ FILE: target/product/aosp_x86_arm.mk ================================================ # # Copyright 2016 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_USE_DYNAMIC_PARTITIONS := true # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) # Enable mainline checking ifeq (aosp_x86_arm,$(TARGET_PRODUCT)) PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed endif # TODO (b/138382074): remove following setting after enable product/system_ext PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ system/product/% \ system/system_ext/% # # All components inherited here go to system_ext image # $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) # # All components inherited here go to product image # $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk) # # All components inherited here go to vendor image # $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_arm/device.mk) PRODUCT_NAME := aosp_x86_arm PRODUCT_DEVICE := generic_x86_arm PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on IA Emulator ================================================ FILE: target/product/app_function_extensions.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # The app function sidecar extensions # /system_ext packages PRODUCT_PACKAGES += \ com.android.extensions.appfunctions \ appfunctions.extension.xml ================================================ FILE: target/product/base.mk ================================================ # # Copyright (C) 2018 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile is suitable to inherit by products that don't need to be split # up by partition. $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk) ================================================ FILE: target/product/base_product.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Base modules and settings for the product partition. PRODUCT_PACKAGES += \ build_flag_product \ fs_config_dirs_product \ fs_config_files_product \ group_product \ ModuleMetadata \ passwd_product \ product_compatibility_matrix.xml \ product_manifest.xml \ selinux_policy_product \ product-build.prop \ # Packages included only for eng or userdebug builds, previously debug tagged PRODUCT_PACKAGES_DEBUG += \ adb_keys \ ================================================ FILE: target/product/base_system.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Base modules and settings for the system partition. PRODUCT_PACKAGES += \ abx \ aconfigd-system \ adbd_system_api \ aflags \ am \ android.hidl.base-V1.0-java \ android.hidl.manager-V1.0-java \ android.system.suspend-service \ android.test.base \ android.test.mock \ android.test.runner \ apexd \ appops \ app_process \ appwidget \ atrace \ audioserver \ BackupRestoreConfirmation \ bcc \ blank_screen \ blkid \ bmgr \ bootanimation \ bootstat \ boringssl_self_test \ bpfloader \ bu \ bugreport \ bugreportz \ build_flag_system \ cgroups.json \ charger \ cmd \ com.android.adbd \ com.android.adservices \ com.android.appsearch \ com.android.bt \ com.android.configinfrastructure \ com.android.conscrypt \ com.android.devicelock \ com.android.extservices \ com.android.healthfitness \ com.android.i18n \ com.android.ipsec \ com.android.location.provider \ com.android.media \ com.android.media.swcodec \ com.android.mediaprovider \ com.android.ondevicepersonalization \ com.android.os.statsd \ com.android.permission \ com.android.resolv \ com.android.rkpd \ com.android.neuralnetworks \ com.android.scheduling \ com.android.sdkext \ com.android.tethering \ $(RELEASE_PACKAGE_TZDATA_MODULE) \ com.android.uwb \ com.android.virt \ com.android.wifi \ ContactsProvider \ content \ CtsShimPrebuilt \ CtsShimPrivPrebuilt \ debuggerd\ device_config \ dmctl \ dnsmasq \ dmesgd \ DownloadProvider \ dpm \ dump.erofs \ dumpstate \ dumpsys \ E2eeContactKeysProvider \ e2fsck \ enhanced-confirmation.xml \ ExtShared \ flags_health_check \ framework-graphics \ framework-location \ framework-minus-apex \ framework-minus-apex-install-dependencies \ framework-sysconfig.xml \ fsck.erofs \ fsck_msdos \ fsverity-release-cert-der \ fs_config_files_system \ fs_config_dirs_system \ gpu_counter_producer \ group_system \ gsid \ gsi_tool \ heapprofd \ heapprofd_client \ gatekeeperd \ gpuservice \ hid \ idmap2 \ idmap2d \ ime \ ims-common \ incident \ incidentd \ incident_helper \ incident-helper-cmd \ init.environ.rc \ init_system \ initial-package-stopped-states.xml \ input \ installd \ IntentResolver \ ip \ iptables \ javax.obex \ kcmdlinectrl \ keystore2 \ credstore \ ld.mc \ libaaudio \ libalarm_jni \ libamidi \ libandroid \ libandroidfw \ libandroid_runtime \ libandroid_servers \ libartpalette-system \ libaudioeffect_jni \ libbinder \ libbinder_ndk \ libbinder_rpc_unstable \ libc.bootstrap \ libcamera2ndk \ libcutils \ libdl.bootstrap \ libdl_android.bootstrap \ libdrmframework \ libdrmframework_jni \ libEGL \ libETC1 \ libfdtrack \ libFFTEm \ libfilterfw \ libgatekeeper \ libGLESv1_CM \ libGLESv2 \ libGLESv3 \ libgui \ libhardware \ libhardware_legacy \ libincident \ libinput \ libinputflinger \ libiprouteutil \ libjnigraphics \ libjpeg \ liblog \ libm.bootstrap \ libmedia \ libmedia_jni \ libmediandk \ libmonkey_jni \ libmtp \ libnetd_client \ libnetlink \ libnetutils \ libneuralnetworks_packageinfo \ libOpenMAXAL \ libOpenSLES \ libpdfium \ libpower \ libpowermanager \ libradio_metadata \ librtp_jni \ libsensorservice \ libsfplugin_ccodec \ libskia \ libsonic \ libsonivox \ libsoundpool \ libspeexresampler \ libsqlite \ libstagefright \ libstagefright_foundation \ libstagefright_omx \ libstdc++ \ libsysutils \ libui \ libusbhost \ libutils \ libvintf_jni \ libvulkan \ libwilhelm \ linker \ llkd \ llndk_libs \ lmkd \ LocalTransport \ locksettings \ logcat \ logd \ lpdump \ lshal \ mdnsd \ mediacodec.policy \ mediaextractor \ mediametrics \ media_profiles_V1_0.dtd \ MediaProviderLegacy \ mediaserver \ mke2fs \ mkfs.erofs \ monkey \ misctrl \ mtectrl \ ndc \ netd \ NetworkStack \ odsign \ org.apache.http.legacy \ otacerts \ PackageInstaller \ package-shareduid-allowlist.xml \ passwd_system \ pbtombstone \ perfetto \ perfetto-extras \ ping \ ping6 \ pintool \ platform.xml \ pm \ prefetch \ preinstalled-packages-asl-files.xml \ preinstalled-packages-platform.xml \ preinstalled-packages-strict-signature.xml \ privapp-permissions-platform.xml \ prng_seeder \ recovery-persist \ resize2fs \ rss_hwm_reset \ run-as \ sanitizer.libraries.txt \ schedtest \ screencap \ sdcard \ secdiscard \ SecureElement \ selinux_policy_system \ sensorservice \ service \ servicemanager \ services \ settings \ SettingsProvider \ sfdo \ sgdisk \ Shell \ shell_and_utilities_system \ sm \ snapuserd \ storaged \ surfaceflinger \ svc \ system-build.prop \ task_profiles.json \ tc \ telecom \ telephony-common \ tombstoned \ traced \ traced_probes \ tradeinmode \ tune2fs \ uiautomator \ uinput \ uncrypt \ usbd \ vdc \ vintf \ voip-common \ vold \ watchdogd \ wificond \ wifi.rc \ wm \ # When we release crashrecovery module ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true) PRODUCT_PACKAGES += \ com.android.crashrecovery \ else PRODUCT_PACKAGES += \ framework-platformcrashrecovery \ endif # When we release ondeviceintelligence in neuralnetworks module ifneq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true) PRODUCT_PACKAGES += \ framework-ondeviceintelligence-platform endif # When we release uprobestats module ifeq ($(RELEASE_UPROBESTATS_MODULE),true) PRODUCT_PACKAGES += \ com.android.uprobestats \ else PRODUCT_PACKAGES += \ uprobestats \ libuprobestats_client \ endif # These packages are not used on Android TV ifneq ($(PRODUCT_IS_ATV),true) PRODUCT_PACKAGES += \ $(RELEASE_PACKAGE_SOUND_PICKER) \ endif # Product does not support Dynamic System Update ifneq ($(PRODUCT_NO_DYNAMIC_SYSTEM_UPDATE),true) PRODUCT_PACKAGES += \ DynamicSystemInstallationService \ endif # Check if the build supports NFC apex or not ifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci) PRODUCT_PACKAGES += \ framework-nfc \ NfcNci else PRODUCT_PACKAGES += \ com.android.nfcservices endif # Check if the build supports Profiling module ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true) PRODUCT_PACKAGES += \ com.android.profiling endif ifeq ($(RELEASE_USE_WEBVIEW_BOOTSTRAP_MODULE),true) PRODUCT_PACKAGES += \ com.android.webview.bootstrap endif # Only add the jar when it is not in the Tethering module. Otherwise, # it will be added via com.android.tethering ifneq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true) PRODUCT_PACKAGES += \ framework-connectivity-b endif ifneq (,$(RELEASE_RANGING_STACK)) PRODUCT_PACKAGES += \ com.android.ranging endif ifeq ($(RELEASE_MEMORY_MANAGEMENT_DAEMON),true) PRODUCT_PACKAGES += \ mm_daemon else PRODUCT_PACKAGES += \ init-mmd-prop.rc endif # VINTF data for system image PRODUCT_PACKAGES += \ system_manifest.xml \ system_compatibility_matrix.xml \ # hwservicemanager is now installed on system_ext, but apexes might be using # old libraries that are expecting it to be installed on system. This allows # those apexes to continue working. The symlink can be removed once we are sure # there are no devices using hwservicemanager (when Android V launching devices # are no longer supported for dessert upgrades). PRODUCT_PACKAGES += \ hwservicemanager_compat_symlink_module \ PRODUCT_PACKAGES_ARM64 := libclang_rt.hwasan \ libclang_rt.hwasan.bootstrap \ libc_hwasan \ # Jacoco agent JARS to be built and installed, if any. ifeq ($(EMMA_INSTRUMENT),true) ifneq ($(EMMA_INSTRUMENT_STATIC),true) # For instrumented build, if Jacoco is not being included statically # in instrumented packages then include Jacoco classes in the product # packages. PRODUCT_PACKAGES += jacocoagent ifneq ($(EMMA_INSTRUMENT_FRAMEWORK),true) # For instrumented build, if Jacoco is not being included statically # in instrumented packages and has not already been included in the # bootclasspath via ART_APEX_JARS then include Jacoco classes into the # bootclasspath. PRODUCT_BOOT_JARS += jacocoagent endif # EMMA_INSTRUMENT_FRAMEWORK endif # EMMA_INSTRUMENT_STATIC endif # EMMA_INSTRUMENT ifeq (,$(DISABLE_WALLPAPER_BACKUP)) PRODUCT_PACKAGES += \ WallpaperBackup endif PRODUCT_PACKAGES += \ libEGL_angle \ libGLESv1_CM_angle \ libGLESv2_angle # For testing purposes ifeq ($(FORCE_AUDIO_SILENT), true) PRODUCT_SYSTEM_PROPERTIES += ro.audio.silent=1 endif # Host tools to install PRODUCT_HOST_PACKAGES += \ BugReport \ adb \ adevice \ atest \ bcc \ bit \ dump.erofs \ e2fsck \ fastboot \ flags_health_check \ fsck.erofs \ icu-data_host_i18n_apex \ tzdata_icu_res_files_host_prebuilts \ idmap2 \ incident_report \ ld.mc \ lpdump \ mke2fs \ mkfs.erofs \ pbtombstone \ resize2fs \ sgdisk \ sqlite3 \ tinyplay \ tune2fs \ unwind_info \ unwind_reg_info \ unwind_symbols \ tzdata_host \ tzdata_host_tzdata_apex \ tzlookup.xml_host_tzdata_apex \ tz_version_host \ tz_version_host_tzdata_apex \ # For art-tools, if the dependencies have changed, please sync them to art/Android.bp as well. PRODUCT_HOST_PACKAGES += \ ahat \ dexdump \ hprof-conv # A subset of the tools are disabled when HOST_PREFER_32_BIT is defined as make reports that # they are not supported on host (b/129323791). This is likely due to art_apex disabling host # APEX builds when HOST_PREFER_32_BIT is set (b/120617876). ifneq ($(HOST_PREFER_32_BIT),true) PRODUCT_HOST_PACKAGES += \ dexlist \ oatdump endif PRODUCT_PACKAGES += init.usb.rc init.usb.configfs.rc PRODUCT_PACKAGES += etc_hosts PRODUCT_PACKAGES += init.zygote32.rc PRODUCT_VENDOR_PROPERTIES += ro.zygote?=zygote32 PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0 PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1 PRODUCT_SYSTEM_PROPERTIES += ro.surface_flinger.game_default_frame_rate_override=60 # Include kernel configs. PRODUCT_PACKAGES += \ approved-ogki-builds.xml \ kernel-lifetimes.xml # Packages included only for eng or userdebug builds, previously debug tagged PRODUCT_PACKAGES_DEBUG := \ adevice_fingerprint \ arping \ dmuserd \ evemu-record \ idlcli \ init-debug.rc \ iotop \ iperf3 \ iw \ layertracegenerator \ libclang_rt.ubsan_standalone \ logpersist.start \ logtagd.rc \ ot-cli-ftd \ ot-ctl \ overlay_remounter \ procrank \ profcollectd \ profcollectctl \ record_binder \ servicedispatcher \ showmap \ snapshotctl \ sqlite3 \ ss \ start_with_lockagent \ strace \ su \ sanitizer-status \ tracepath \ tracepath6 \ traceroute6 \ unwind_info \ unwind_reg_info \ unwind_symbols \ # The set of packages whose code can be loaded by the system server. PRODUCT_SYSTEM_SERVER_APPS += \ SettingsProvider \ ifeq (,$(DISABLE_WALLPAPER_BACKUP)) PRODUCT_SYSTEM_SERVER_APPS += \ WallpaperBackup endif PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE := \ libdumpcoverage PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ frameworks/base/config/preloaded-classes:system/etc/preloaded-classes) # Enable dirty image object binning to reduce dirty pages in the image. PRODUCT_PACKAGES += dirty-image-objects # Enable go/perfetto-persistent-tracing for eng builds ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT))) PRODUCT_PRODUCT_PROPERTIES += persist.debug.perfetto.persistent_sysui_tracing_for_bugreport=1 endif $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk) # Ensure all trunk-stable flags are available. $(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk) # Use "image" APEXes always. $(call inherit-product,$(SRC_TARGET_DIR)/product/updatable_apex.mk) $(call soong_config_set, bionic, large_system_property_node, $(RELEASE_LARGE_SYSTEM_PROPERTY_NODE)) $(call soong_config_set, Aconfig, read_from_new_storage, $(RELEASE_READ_FROM_NEW_STORAGE)) $(call soong_config_set, SettingsLib, legacy_avatar_picker_app_enabled, $(if $(RELEASE_AVATAR_PICKER_APP),,true)) ================================================ FILE: target/product/base_system_ext.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Base modules and settings for the system_ext partition. PRODUCT_PACKAGES += \ build_flag_system_ext \ fs_config_dirs_system_ext \ fs_config_files_system_ext \ group_system_ext \ passwd_system_ext \ SatelliteClient \ selinux_policy_system_ext \ system_ext_manifest.xml \ system_ext-build.prop \ # Base modules when shipping api level is less than or equal to 34 PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \ hwservicemanager \ android.hidl.allocator@1.0-service \ android.hidl.memory@1.0-impl \ # AppFunction Extensions ifneq (,$(RELEASE_APPFUNCTION_SIDECAR)) $(call inherit-product, $(SRC_TARGET_DIR)/product/app_function_extensions.mk) endif ================================================ FILE: target/product/base_vendor.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Base modules and settings for recovery. PRODUCT_PACKAGES += \ adbd.recovery \ build_flag_vendor \ cgroups.recovery.json \ charger.recovery \ init_second_stage.recovery \ ld.config.recovery.txt \ linker.recovery \ otacerts.recovery \ recovery \ servicemanager.recovery \ shell_and_utilities_recovery \ watchdogd.recovery \ PRODUCT_VENDOR_PROPERTIES += \ ro.recovery.usb.vid?=18D1 \ ro.recovery.usb.adb.pid?=D001 \ ro.recovery.usb.fastboot.pid?=4EE0 \ # These had been pulled in via init_second_stage.recovery, but may not be needed. PRODUCT_HOST_PACKAGES += \ e2fsdroid \ mke2fs \ sload_f2fs \ make_f2fs \ PRODUCT_HOST_PACKAGES += \ icu-data_host_i18n_apex # Base modules and settings for the vendor partition. PRODUCT_PACKAGES += \ com.android.hardware.cas \ boringssl_self_test_vendor \ dumpsys_vendor \ fs_config_files_nonsystem \ fs_config_dirs_nonsystem \ gralloc.default \ group_odm \ group_vendor \ init_vendor \ libbundlewrapper \ libclearkeycasplugin \ libdownmix \ libdrmclearkeyplugin \ libdynproc \ libeffectproxy \ libeffects \ libhapticgenerator \ libldnhncr \ libreference-ril \ libreverbwrapper \ libril \ libvisualizer \ passwd_odm \ passwd_vendor \ selinux_policy_nonsystem \ selinux_policy_vendor \ selinux_policy_odm \ shell_and_utilities_vendor \ odm-build.prop \ # libhealthloop BPF filter. This is in base_vendor.mk because libhealthloop must # be a static library and because the Android build system ignores 'required' # sections for static libraries. PRODUCT_PACKAGES += filterPowerSupplyEvents.o # Base modules when shipping api level is less than or equal to 34 PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \ android.hidl.memory@1.0-impl.vendor \ # OMX not supported for 64bit_only builds # Only supported when SHIPPING_API_LEVEL is less than or equal to 33 ifneq ($(TARGET_SUPPORTS_OMX_SERVICE),false) PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33 += \ android.hardware.media.omx@1.0-service \ endif # Base modules when shipping api level is less than or equal to 33 PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33 += \ android.hardware.cas@1.2-service \ # Base modules when shipping api level is less than or equal to 29 PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29 += \ android.hardware.configstore@1.1-service \ vndservice \ vndservicemanager \ # VINTF data for vendor image PRODUCT_PACKAGES += \ vendor_compatibility_matrix.xml \ # Base modules and settings for the debug ramdisk, which is then packed # into a boot-debug.img and a vendor_boot-debug.img. PRODUCT_PACKAGES += \ adb_debug.prop \ userdebug_plat_sepolicy.cil # On eng or userdebug builds, build in perf-setup-sh by default. ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) PRODUCT_PACKAGES += \ perf-setup-sh endif ================================================ FILE: target/product/build_variables.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file contains the trunk-stable flags that should be exported to all # Android targets. # Control libbinder client caching $(call soong_config_set, libbinder, release_libbinder_client_cache, $(RELEASE_LIBBINDER_CLIENT_CACHE)) # Control caching while adding service in libbinder cache $(call soong_config_set, libbinder, release_libbinder_addservice_cache, $(RELEASE_LIBBINDER_ADDSERVICE_CACHE)) # Remove static list in libbinder cache $(call soong_config_set, libbinder, release_libbinder_remove_cache_static_list, $(RELEASE_LIBBINDER_REMOVE_CACHE_STATIC_LIST)) # Use the configured release of sqlite $(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3)) # Use the configured MessageQueue implementation $(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION)) # Use the configured version of WebView $(call soong_config_set, webview, release_package_webview_version, $(RELEASE_PACKAGE_WEBVIEW_VERSION)) # Use the configured version of Cronet $(call soong_config_set,cronet,enable_cronet_tot,$(RELEASE_ENABLE_TOT_CRONET)) ================================================ FILE: target/product/cfi-common.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a set of common components to enable CFI for (across # compatible product configs) PRODUCT_CFI_INCLUDE_PATHS := \ device/generic/goldfish/wifi/wpa_supplicant_8_lib \ device/google/cuttlefish/guest/libs/wpa_supplicant_8_lib \ external/tinyxml2 \ external/wpa_supplicant_8 \ frameworks/av/camera \ frameworks/av/media \ frameworks/av/services \ frameworks/minikin \ hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib \ hardware/synaptics/wlan/synadhd/wpa_supplicant_8_lib \ hardware/interfaces/nfc \ hardware/qcom/wlan/qcwcn/wpa_supplicant_8_lib \ hardware/qcom/wlan/legacy/qcwcn/wpa_supplicant_8_lib \ hardware/qcom/wlan/wcn6740/qcwcn/wpa_supplicant_8_lib \ hardware/interfaces/keymaster \ hardware/interfaces/security \ packages/modules/Bluetooth/system \ system/chre \ system/core/libnetutils \ system/libziparchive \ system/gatekeeper \ system/keymaster \ system/nfc \ system/security \ ================================================ FILE: target/product/core_64_bit.mk ================================================ # # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Inherit from this product for devices that support 64-bit apps using: # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) # The inheritance for this must come before the inheritance chain that leads # to core_minimal.mk # For now this will allow 64-bit apps, but still compile all apps with JNI # for 32-bit only. # Copy the 64-bit primary, 32-bit secondary zygote startup script PRODUCT_PACKAGES += init.zygote64.rc init.zygote64_32.rc # Set the zygote property to select the 64-bit primary, 32-bit secondary script # This line must be parsed before the one in core_minimal.mk ifeq ($(ZYGOTE_FORCE_64),true) PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64 else PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64_32 endif TARGET_SUPPORTS_32_BIT_APPS := true TARGET_SUPPORTS_64_BIT_APPS := true ================================================ FILE: target/product/core_64_bit_only.mk ================================================ # # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Inherit from this product for devices that support only 64-bit apps using: # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) # The inheritance for this must come before the inheritance chain that leads # to core_minimal.mk. # Copy the 64-bit zygote startup script PRODUCT_PACKAGES += init.zygote64.rc # Set the zygote property to select the 64-bit script. # This line must be parsed before the one in core_minimal.mk PRODUCT_VENDOR_PROPERTIES += ro.zygote=zygote64 # A 64-bit-only platform does not have dex2oat32, so make sure dex2oat64 is # used for dexopt. PRODUCT_VENDOR_PROPERTIES += dalvik.vm.dex2oat64.enabled=true TARGET_SUPPORTS_32_BIT_APPS := false TARGET_SUPPORTS_64_BIT_APPS := true TARGET_SUPPORTS_OMX_SERVICE := false ================================================ FILE: target/product/core_minimal.mk ================================================ # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This product is the base of a generic media-capable device, which # means most android products, but excludes wearables. # # Note: Do not add any contents directly to this file. Choose either # media_ depending on partition also consider base_.mk or # handheld_.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk) PRODUCT_BRAND := generic PRODUCT_DEVICE := generic PRODUCT_NAME := core ================================================ FILE: target/product/core_no_zygote.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Inherit from this product for devices that do not include a zygote using: # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) # The inheritance for this must come before the inheritance chain that leads # to core_minimal.mk. # Copy the no-zygote startup script PRODUCT_COPY_FILES += system/core/rootdir/init.no_zygote.rc:system/etc/init/hw/init.no_zygote.rc # Set the zygote property to select the no-zygote script. # This line must be parsed before the one in core_minimal.mk PRODUCT_VENDOR_PROPERTIES += ro.zygote=no_zygote TARGET_SUPPORTS_32_BIT_APPS := false TARGET_SUPPORTS_64_BIT_APPS := false ================================================ FILE: target/product/default_art_config.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file contains product config for the ART module that is common for # platform and unbundled builds. ifeq ($(ART_APEX_JARS),) $(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable) endif # Order of the jars on BOOTCLASSPATH follows: # 1. ART APEX jars # 2. System jars # 3. System_ext jars # 4. Non-updatable APEX jars # 5. Updatable APEX jars # # ART APEX jars (1) are defined in ART_APEX_JARS. System and system_ext boot jars are defined below # in PRODUCT_BOOT_JARS. All other non-art APEX boot jars are part of the PRODUCT_APEX_BOOT_JARS. # # The actual runtime ordering matching above is determined by derive_classpath service at runtime. # See packages/modules/SdkExtensions/README.md for more details. # The order of PRODUCT_BOOT_JARS matters for runtime class lookup performance. PRODUCT_BOOT_JARS := \ $(ART_APEX_JARS) # List of jars to be included in the ART boot image for testing. # DO NOT reorder this list. The order must match the one described above. # Note: We use the host variant of "core-icu4j" and "conscrypt" for testing. PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS := \ $(ART_APEX_JARS) \ platform:core-icu4j-host \ platform:conscrypt-host \ # /system and /system_ext boot jars. PRODUCT_BOOT_JARS += \ framework-minus-apex \ framework-graphics \ framework-location \ ext \ telephony-common \ voip-common \ ims-common # APEX boot jars. Keep the list sorted by module names and then library names. # Note: If the existing apex introduces the new jar, also add it to # PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY below. # Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage. # Note: For modules available in Q, DO NOT add new entries here. PRODUCT_APEX_BOOT_JARS := \ com.android.adservices:framework-adservices \ com.android.adservices:framework-sdksandbox \ com.android.appsearch:framework-appsearch \ com.android.bt:framework-bluetooth \ com.android.configinfrastructure:framework-configinfrastructure \ com.android.conscrypt:conscrypt \ com.android.devicelock:framework-devicelock \ com.android.healthfitness:framework-healthfitness \ com.android.i18n:core-icu4j \ com.android.ipsec:android.net.ipsec.ike \ com.android.media:updatable-media \ com.android.mediaprovider:framework-mediaprovider \ com.android.mediaprovider:framework-pdf \ com.android.mediaprovider:framework-pdf-v \ com.android.mediaprovider:framework-photopicker \ com.android.ondevicepersonalization:framework-ondevicepersonalization \ com.android.os.statsd:framework-statsd \ com.android.permission:framework-permission \ com.android.permission:framework-permission-s \ com.android.scheduling:framework-scheduling \ com.android.sdkext:framework-sdkextensions \ com.android.tethering:framework-connectivity \ com.android.tethering:framework-connectivity-t \ com.android.tethering:framework-tethering \ com.android.uwb:framework-uwb \ com.android.virt:framework-virtualization \ com.android.wifi:framework-wifi \ # When crashrecovery module is ready use apex jar # else put the platform jar in system ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true) PRODUCT_APEX_BOOT_JARS += \ com.android.crashrecovery:framework-crashrecovery \ else PRODUCT_BOOT_JARS += \ framework-platformcrashrecovery \ endif # When we release ondeviceintelligence in NeuralNetworks module ifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true) PRODUCT_APEX_BOOT_JARS += \ com.android.neuralnetworks:framework-ondeviceintelligence \ else PRODUCT_BOOT_JARS += \ framework-ondeviceintelligence-platform \ endif # Check if the build supports NFC apex or not ifeq ($(RELEASE_PACKAGE_NFC_STACK),NfcNci) PRODUCT_BOOT_JARS += \ framework-nfc else PRODUCT_APEX_BOOT_JARS += \ com.android.nfcservices:framework-nfc $(call soong_config_set,bootclasspath,nfc_apex_bootclasspath_fragment,true) endif # Check if build supports Profiling module. ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true) PRODUCT_APEX_BOOT_JARS += \ com.android.profiling:framework-profiling \ endif ifneq (,$(RELEASE_RANGING_STACK)) PRODUCT_APEX_BOOT_JARS += \ com.android.uwb:framework-ranging \ $(call soong_config_set,bootclasspath,release_ranging_stack,true) endif # Check if VCN should be built into the tethering module or not ifeq ($(RELEASE_MOVE_VCN_TO_MAINLINE),true) PRODUCT_APEX_BOOT_JARS += \ com.android.tethering:framework-connectivity-b \ else PRODUCT_BOOT_JARS += \ framework-connectivity-b \ endif # List of system_server classpath jars delivered via apex. # Keep the list sorted by module names and then library names. # Note: For modules available in Q, DO NOT add new entries here. PRODUCT_APEX_SYSTEM_SERVER_JARS := \ com.android.adservices:service-adservices \ com.android.adservices:service-sdksandbox \ com.android.appsearch:service-appsearch \ com.android.art:service-art \ com.android.configinfrastructure:service-configinfrastructure \ com.android.healthfitness:service-healthfitness \ com.android.media:service-media-s \ com.android.ondevicepersonalization:service-ondevicepersonalization \ com.android.permission:service-permission \ com.android.rkpd:service-rkp \ # When we release crashrecovery module ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true) PRODUCT_APEX_SYSTEM_SERVER_JARS += \ com.android.crashrecovery:service-crashrecovery \ endif # When we release ondeviceintelligence in NeuralNetworks module ifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true) PRODUCT_APEX_SYSTEM_SERVER_JARS += \ com.android.neuralnetworks:service-ondeviceintelligence endif ifeq ($(RELEASE_AVF_ENABLE_LLPVM_CHANGES),true) PRODUCT_APEX_SYSTEM_SERVER_JARS += com.android.virt:service-virtualization endif # Use $(wildcard) to avoid referencing the profile in thin manifests that don't have the # art project. ifneq (,$(wildcard art)) PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += art/build/boot/boot-image-profile.txt endif # List of jars on the platform that system_server loads dynamically using separate classloaders. # Keep the list sorted library names. PRODUCT_STANDALONE_SYSTEM_SERVER_JARS := \ # List of jars delivered via apex that system_server loads dynamically using separate classloaders. # Keep the list sorted by module names and then library names. # Note: For modules available in Q, DO NOT add new entries here. PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS := \ com.android.bt:service-bluetooth \ com.android.devicelock:service-devicelock \ com.android.os.statsd:service-statsd \ com.android.scheduling:service-scheduling \ com.android.tethering:service-connectivity \ com.android.uwb:service-uwb \ com.android.wifi:service-wifi \ # Check if build supports Profiling module. ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true) PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \ com.android.profiling:service-profiling \ endif ifneq (,$(RELEASE_RANGING_STACK)) PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS += \ com.android.uwb:service-ranging endif # Overrides the (apex, jar) pairs above when determining the on-device location. The format is: # ::: PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES := \ platform:framework-minus-apex:platform:framework \ platform:core-icu4j-host:com.android.i18n:core-icu4j \ platform:conscrypt-host:com.android.conscrypt:conscrypt \ # Minimal configuration for running dex2oat (default argument values). # PRODUCT_USES_DEFAULT_ART_CONFIG must be true to enable boot image compilation. PRODUCT_USES_DEFAULT_ART_CONFIG := true PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.image-dex2oat-Xms=64m \ dalvik.vm.image-dex2oat-Xmx=64m \ dalvik.vm.dex2oat-Xms=64m \ dalvik.vm.dex2oat-Xmx=512m \ PRODUCT_ENABLE_UFFD_GC := default ================================================ FILE: target/product/developer_gsi_keys.mk ================================================ # # Copyright (C) 2019 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Device makers who are willing to support booting the public Developer-GSI # in locked state can add the following line into a device.mk to inherit this # makefile. This file will then install the up-to-date GSI public keys into # the first-stage ramdisk to pass verified boot. # # In device///device.mk: # $(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk) # # Currently, the developer GSI images can be downloaded from the following URL: # https://developer.android.com/topic/generic-system-image/releases # PRODUCT_PACKAGES += \ q-developer-gsi.avbpubkey \ r-developer-gsi.avbpubkey \ s-developer-gsi.avbpubkey \ ================================================ FILE: target/product/empty-preloaded-classes ================================================ # Empty preloaded-classes file for automated testing. ================================================ FILE: target/product/empty-profile ================================================ # Empty preloaded-classes file for automated testing. ================================================ FILE: target/product/emulated_storage.mk ================================================ # # Copyright (C) 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_QUOTA_PROJID := 1 PRODUCT_VENDOR_PROPERTIES += external_storage.projid.enabled=1 PRODUCT_FS_CASEFOLD := 1 PRODUCT_VENDOR_PROPERTIES += external_storage.casefold.enabled=1 PRODUCT_VENDOR_PROPERTIES += external_storage.sdcardfs.enabled=0 ================================================ FILE: target/product/full.manifest.xml ================================================ ================================================ FILE: target/product/full.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. $(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) $(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk) DEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml # Enable dynamic partition size PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true # Overrides PRODUCT_NAME := full PRODUCT_DEVICE := generic PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on ARM Emulator ================================================ FILE: target/product/full_base.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build of the emulator, but all those aspects can be overridden # in inherited configurations. PRODUCT_PACKAGES := \ libfwdlockengine \ WAPPushManager PRODUCT_PACKAGES += \ LiveWallpapersPicker \ PhotoTable \ preinstalled-packages-platform-full-base.xml # Net: # Vendors can use the platform-provided network configuration utilities (ip, # iptable, etc.) to configure the Linux networking stack, but these utilities # do not yet include a HIDL interface wrapper. This is a solution on # Android O. PRODUCT_PACKAGES += \ netutils-wrapper-1.0 # Additional settings used in all AOSP builds PRODUCT_VENDOR_PROPERTIES := \ ro.config.ringtone?=Ring_Synth_04.ogg \ ro.config.notification_sound?=pixiedust.ogg # Put en_US first in the list, so make it default. PRODUCT_LOCALES := en_US # Get some sounds $(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk) # Get a list of languages. $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk) # Get everything else from the parent package $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk) # Add adb keys to debuggable AOSP builds (if they exist) $(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk) ================================================ FILE: target/product/full_base_telephony.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. PRODUCT_VENDOR_PROPERTIES := \ keyguard.no_require_sim?=true \ ro.com.android.dataroaming?=true PRODUCT_COPY_FILES := \ device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml \ frameworks/native/data/etc/handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk) ================================================ FILE: target/product/full_x86.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration for a full-featured build of the # Open-Source part of the tree. It's geared toward a US-centric # build quite specifically for the emulator, and might not be # entirely appropriate to inherit from for on-device configurations. # If running on an emulator or some other device that has a LAN connection # that isn't a wifi connection. This will instruct init.rc to enable the # network connection so that you can use it with ADB $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk) DEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml ifdef NET_ETH0_STARTONBOOT PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1 endif # Enable dynamic partition size PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true # Overrides PRODUCT_NAME := full_x86 PRODUCT_DEVICE := generic_x86 PRODUCT_BRAND := Android PRODUCT_MODEL := AOSP on IA Emulator ================================================ FILE: target/product/fullmte.mk ================================================ # # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Enables more comprehensive detection of memory errors on hardware that # supports the ARM Memory Tagging Extension (MTE), by building the image with # MTE stack instrumentation and forcing MTE on in SYNC mode in all processes. # For more details, see: # https://source.android.com/docs/security/test/memory-safety/arm-mte ifeq ($(filter memtag_heap,$(SANITIZE_TARGET)),) SANITIZE_TARGET := $(strip $(SANITIZE_TARGET) memtag_heap memtag_stack memtag_globals) SANITIZE_TARGET_DIAG := $(strip $(SANITIZE_TARGET_DIAG) memtag_heap) endif PRODUCT_PRODUCT_PROPERTIES += persist.arm64.memtag.default=sync PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE := 131072 ================================================ FILE: target/product/generic/Android.bp ================================================ generic_rootdirs = [ "apex", "bootstrap-apex", "config", "data", "data_mirror", "debug_ramdisk", "dev", "linkerconfig", "metadata", "mnt", "odm", "odm_dlkm", "oem", "postinstall", "proc", "second_stage_resources", "storage", "sys", "system", "system_dlkm", "tmp", "vendor", "vendor_dlkm", ] android_rootdirs = [ "system_ext", "product", ] generic_symlinks = [ { target: "/system/bin/init", name: "init", }, { target: "/system/etc", name: "etc", }, { target: "/system/bin", name: "bin", }, { target: "/vendor", name: "system/vendor", }, { target: "/system_dlkm/lib/modules", name: "system/lib/modules", }, { target: "/data/user_de/0/com.android.shell/files/bugreports", name: "bugreports", }, { target: "/sys/kernel/debug", name: "d", }, { target: "/storage/self/primary", name: "sdcard", }, { target: "/product/etc/security/adb_keys", name: "adb_keys", }, // For Treble Generic System Image (GSI), system-as-root GSI needs to work on both devices with // and without /odm partition. Those symlinks are for devices without /odm partition. For // devices with /odm partition, mount odm.img under /odm will hide those symlinks. { target: "/vendor/odm/app", name: "odm/app", }, { target: "/vendor/odm/bin", name: "odm/bin", }, { target: "/vendor/odm/etc", name: "odm/etc", }, { target: "/vendor/odm/firmware", name: "odm/firmware", }, { target: "/vendor/odm/framework", name: "odm/framework", }, { target: "/vendor/odm/lib", name: "odm/lib", }, { target: "/vendor/odm/lib64", name: "odm/lib64", }, { target: "/vendor/odm/overlay", name: "odm/overlay", }, { target: "/vendor/odm/priv-app", name: "odm/priv-app", }, { target: "/vendor/odm/usr", name: "odm/usr", }, ] android_symlinks = [ { target: "/product", name: "system/product", }, { target: "/system_ext", name: "system/system_ext", }, { target: "/data/cache", name: "cache", }, { target: "/odm/odm_dlkm/etc", name: "odm_dlkm/etc", }, { target: "/vendor/vendor_dlkm/etc", name: "vendor_dlkm/etc", }, ] extra_vendor_symlinks = [ // Some vendors still haven't cleaned up all device specific directories under root! // TODO(b/111434759, b/111287060) SoC specific hacks { target: "/vendor/lib/dsp", name: "dsp", }, { target: "/mnt/vendor/persist", name: "persist", }, { target: "/vendor/firmware_mnt", name: "firmware", }, ] filegroup { name: "generic_system_sign_key", srcs: [":avb_testkey_rsa4096"], } phony { name: "generic_system_fonts", required: [ "AndroidClock.ttf", "CarroisGothicSC-Regular.ttf", "ComingSoon.ttf", "CutiveMono.ttf", "DancingScript-Regular.ttf", "DroidSansMono.ttf", "NotoColorEmoji.ttf", "NotoColorEmojiFlags.ttf", "NotoNaskhArabic-Bold.ttf", "NotoNaskhArabic-Regular.ttf", "NotoNaskhArabicUI-Bold.ttf", "NotoNaskhArabicUI-Regular.ttf", "NotoSansAdlam-VF.ttf", "NotoSansAhom-Regular.otf", "NotoSansAnatolianHieroglyphs-Regular.otf", "NotoSansArmenian-VF.ttf", "NotoSansAvestan-Regular.ttf", "NotoSansBalinese-Regular.ttf", "NotoSansBamum-Regular.ttf", "NotoSansBassaVah-Regular.otf", "NotoSansBatak-Regular.ttf", "NotoSansBengali-VF.ttf", "NotoSansBengaliUI-VF.ttf", "NotoSansBhaiksuki-Regular.otf", "NotoSansBrahmi-Regular.ttf", "NotoSansBuginese-Regular.ttf", "NotoSansBuhid-Regular.ttf", "NotoSansCJK-Regular.ttc", "NotoSansCanadianAboriginal-Regular.ttf", "NotoSansCarian-Regular.ttf", "NotoSansChakma-Regular.otf", "NotoSansCham-Bold.ttf", "NotoSansCham-Regular.ttf", "NotoSansCherokee-Regular.ttf", "NotoSansCoptic-Regular.ttf", "NotoSansCuneiform-Regular.ttf", "NotoSansCypriot-Regular.ttf", "NotoSansDeseret-Regular.ttf", "NotoSansDevanagari-VF.ttf", "NotoSansDevanagariUI-VF.ttf", "NotoSansEgyptianHieroglyphs-Regular.ttf", "NotoSansElbasan-Regular.otf", "NotoSansEthiopic-VF.ttf", "NotoSansGeorgian-VF.ttf", "NotoSansGlagolitic-Regular.ttf", "NotoSansGothic-Regular.ttf", "NotoSansGrantha-Regular.ttf", "NotoSansGujarati-Bold.ttf", "NotoSansGujarati-Regular.ttf", "NotoSansGujaratiUI-Bold.ttf", "NotoSansGujaratiUI-Regular.ttf", "NotoSansGunjalaGondi-Regular.otf", "NotoSansGurmukhi-VF.ttf", "NotoSansGurmukhiUI-VF.ttf", "NotoSansHanifiRohingya-Regular.otf", "NotoSansHanunoo-Regular.ttf", "NotoSansHatran-Regular.otf", "NotoSansHebrew-Bold.ttf", "NotoSansHebrew-Regular.ttf", "NotoSansImperialAramaic-Regular.ttf", "NotoSansInscriptionalPahlavi-Regular.ttf", "NotoSansInscriptionalParthian-Regular.ttf", "NotoSansJavanese-Regular.otf", "NotoSansKaithi-Regular.ttf", "NotoSansKannada-VF.ttf", "NotoSansKannadaUI-VF.ttf", "NotoSansKayahLi-Regular.ttf", "NotoSansKharoshthi-Regular.ttf", "NotoSansKhmer-VF.ttf", "NotoSansKhmerUI-Bold.ttf", "NotoSansKhmerUI-Regular.ttf", "NotoSansKhojki-Regular.otf", "NotoSansLao-Bold.ttf", "NotoSansLao-Regular.ttf", "NotoSansLaoUI-Bold.ttf", "NotoSansLaoUI-Regular.ttf", "NotoSansLepcha-Regular.ttf", "NotoSansLimbu-Regular.ttf", "NotoSansLinearA-Regular.otf", "NotoSansLinearB-Regular.ttf", "NotoSansLisu-Regular.ttf", "NotoSansLycian-Regular.ttf", "NotoSansLydian-Regular.ttf", "NotoSansMalayalam-VF.ttf", "NotoSansMalayalamUI-VF.ttf", "NotoSansMandaic-Regular.ttf", "NotoSansManichaean-Regular.otf", "NotoSansMarchen-Regular.otf", "NotoSansMasaramGondi-Regular.otf", "NotoSansMedefaidrin-VF.ttf", "NotoSansMeeteiMayek-Regular.ttf", "NotoSansMeroitic-Regular.otf", "NotoSansMiao-Regular.otf", "NotoSansModi-Regular.ttf", "NotoSansMongolian-Regular.ttf", "NotoSansMro-Regular.otf", "NotoSansMultani-Regular.otf", "NotoSansMyanmar-Bold.otf", "NotoSansMyanmar-Medium.otf", "NotoSansMyanmar-Regular.otf", "NotoSansMyanmarUI-Bold.otf", "NotoSansMyanmarUI-Medium.otf", "NotoSansMyanmarUI-Regular.otf", "NotoSansNKo-Regular.ttf", "NotoSansNabataean-Regular.otf", "NotoSansNewTaiLue-Regular.ttf", "NotoSansNewa-Regular.otf", "NotoSansOgham-Regular.ttf", "NotoSansOlChiki-Regular.ttf", "NotoSansOldItalic-Regular.ttf", "NotoSansOldNorthArabian-Regular.otf", "NotoSansOldPermic-Regular.otf", "NotoSansOldPersian-Regular.ttf", "NotoSansOldSouthArabian-Regular.ttf", "NotoSansOldTurkic-Regular.ttf", "NotoSansOriya-Bold.ttf", "NotoSansOriya-Regular.ttf", "NotoSansOriyaUI-Bold.ttf", "NotoSansOriyaUI-Regular.ttf", "NotoSansOsage-Regular.ttf", "NotoSansOsmanya-Regular.ttf", "NotoSansPahawhHmong-Regular.otf", "NotoSansPalmyrene-Regular.otf", "NotoSansPauCinHau-Regular.otf", "NotoSansPhagsPa-Regular.ttf", "NotoSansPhoenician-Regular.ttf", "NotoSansRejang-Regular.ttf", "NotoSansRunic-Regular.ttf", "NotoSansSamaritan-Regular.ttf", "NotoSansSaurashtra-Regular.ttf", "NotoSansSharada-Regular.otf", "NotoSansShavian-Regular.ttf", "NotoSansSinhala-VF.ttf", "NotoSansSinhalaUI-VF.ttf", "NotoSansSoraSompeng-Regular.otf", "NotoSansSoyombo-VF.ttf", "NotoSansSundanese-Regular.ttf", "NotoSansSylotiNagri-Regular.ttf", "NotoSansSymbols-Regular-Subsetted.ttf", "NotoSansSymbols-Regular-Subsetted2.ttf", "NotoSansSyriacEastern-Regular.ttf", "NotoSansSyriacEstrangela-Regular.ttf", "NotoSansSyriacWestern-Regular.ttf", "NotoSansTagalog-Regular.ttf", "NotoSansTagbanwa-Regular.ttf", "NotoSansTaiLe-Regular.ttf", "NotoSansTaiTham-Regular.ttf", "NotoSansTaiViet-Regular.ttf", "NotoSansTakri-VF.ttf", "NotoSansTamil-VF.ttf", "NotoSansTamilUI-VF.ttf", "NotoSansTelugu-VF.ttf", "NotoSansTeluguUI-VF.ttf", "NotoSansThaana-Bold.ttf", "NotoSansThaana-Regular.ttf", "NotoSansThai-Bold.ttf", "NotoSansThai-Regular.ttf", "NotoSansThaiUI-Bold.ttf", "NotoSansThaiUI-Regular.ttf", "NotoSansTifinagh-Regular.otf", "NotoSansUgaritic-Regular.ttf", "NotoSansVai-Regular.ttf", "NotoSansWancho-Regular.otf", "NotoSansWarangCiti-Regular.otf", "NotoSansYi-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-BoldItalic.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Regular.ttf", "NotoSerifArmenian-VF.ttf", "NotoSerifBengali-VF.ttf", "NotoSerifCJK-Regular.ttc", "NotoSerifDevanagari-VF.ttf", "NotoSerifDogra-Regular.ttf", "NotoSerifEthiopic-VF.ttf", "NotoSerifGeorgian-VF.ttf", "NotoSerifGujarati-VF.ttf", "NotoSerifGurmukhi-VF.ttf", "NotoSerifHebrew-Bold.ttf", "NotoSerifHebrew-Regular.ttf", "NotoSerifHentaigana.ttf", "NotoSerifKannada-VF.ttf", "NotoSerifKhmer-Bold.otf", "NotoSerifKhmer-Regular.otf", "NotoSerifLao-Bold.ttf", "NotoSerifLao-Regular.ttf", "NotoSerifMalayalam-VF.ttf", "NotoSerifMyanmar-Bold.otf", "NotoSerifMyanmar-Regular.otf", "NotoSerifNyiakengPuachueHmong-VF.ttf", "NotoSerifSinhala-VF.ttf", "NotoSerifTamil-VF.ttf", "NotoSerifTelugu-VF.ttf", "NotoSerifThai-Bold.ttf", "NotoSerifThai-Regular.ttf", "NotoSerifTibetan-VF.ttf", "NotoSerifYezidi-VF.ttf", "Roboto-Regular.ttf", "RobotoFlex-Regular.ttf", "RobotoStatic-Regular.ttf", "SourceSansPro-Bold.ttf", "SourceSansPro-BoldItalic.ttf", "SourceSansPro-Italic.ttf", "SourceSansPro-Regular.ttf", "SourceSansPro-SemiBold.ttf", "SourceSansPro-SemiBoldItalic.ttf", "font_fallback.xml", "fonts.xml", ], } android_filesystem_defaults { name: "system_ext_image_defaults", deps: [ /////////////////////////////////////////// // base_system_ext /////////////////////////////////////////// "build_flag_system_ext", "fs_config_dirs_system_ext", "fs_config_files_system_ext", "group_system_ext", "passwd_system_ext", "SatelliteClient", "selinux_policy_system_ext", "system_ext_manifest.xml", "system_ext-build.prop", // Base modules when shipping api level is less than or equal to 34 "hwservicemanager", "android.hidl.allocator@1.0-service", /////////////////////////////////////////// // media_system_ext /////////////////////////////////////////// "StatementService", /////////////////////////////////////////// // window_extensions_base /////////////////////////////////////////// "androidx.window.extensions", "androidx.window.sidecar", /////////////////////////////////////////// // base_system /////////////////////////////////////////// "charger", ] + select(release_flag("RELEASE_APPFUNCTION_SIDECAR"), { true: [ "com.android.extensions.appfunctions", "appfunctions.extension.xml", ], default: [], }), } android_filesystem_defaults { name: "product_image_defaults", deps: [ /////////////////////////////////////////// // media_product /////////////////////////////////////////// "webview", /////////////////////////////////////////// // base_product /////////////////////////////////////////// // Base modules and settings for the product partition. "build_flag_product", "fs_config_dirs_product", "fs_config_files_product", "group_product", "ModuleMetadata", "passwd_product", "product_compatibility_matrix.xml", "product_manifest.xml", "selinux_policy_product", "product-build.prop", // AUDIO "frameworks_sounds", ] + select(product_variable("debuggable"), { // Packages included only for eng or userdebug builds, previously debug tagged true: ["adb_keys"], default: [], }), } system_image_fsverity_default = { inputs: [ "etc/boot-image.prof", "etc/classpaths/*.pb", "etc/dirty-image-objects", "etc/preloaded-classes", "framework/*", "framework/*/*", // framework/{arch} "framework/oat/*/*", // framework/oat/{arch} ], libs: [":framework-res{.export-package.apk}"], } soong_config_module_type { name: "system_image_defaults", module_type: "android_filesystem_defaults", config_namespace: "ANDROID", bool_variables: ["TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS"], properties: ["symlinks"], } genrule { name: "plat_and_vendor_file_contexts", device_common_srcs: [ ":plat_file_contexts", ":vendor_file_contexts", ], out: ["file_contexts"], cmd: "cat $(in) > $(out)", } system_image_defaults { name: "system_image_defaults", partition_name: "system", base_dir: "system", stem: "system.img", no_full_install: true, dirs: generic_rootdirs, soong_config_variables: { TARGET_ADD_ROOT_EXTRA_VENDOR_SYMLINKS: { symlinks: generic_symlinks + extra_vendor_symlinks, conditions_default: { symlinks: generic_symlinks, }, }, }, file_contexts: ":plat_and_vendor_file_contexts", linker_config: { gen_linker_config: true, linker_config_srcs: [":system_linker_config_json_file"], }, fsverity: { inputs: select(soong_config_variable("ANDROID", "PRODUCT_FSVERITY_GENERATE_METADATA"), { true: [ "etc/boot-image.prof", "etc/classpaths/*.pb", "etc/dirty-image-objects", "etc/preloaded-classes", "framework/*", "framework/*/*", // framework/{arch} "framework/oat/*/*", // framework/oat/{arch} ], default: [], }), libs: select(soong_config_variable("ANDROID", "PRODUCT_FSVERITY_GENERATE_METADATA"), { true: [":framework-res{.export-package.apk}"], default: [], }), }, build_logtags: true, gen_aconfig_flags_pb: true, compile_multilib: "both", use_avb: true, avb_private_key: ":generic_system_sign_key", avb_algorithm: "SHA256_RSA4096", avb_hash_algorithm: "sha256", rollback_index_location: 1, deps: [ "abx", "aconfigd-system", "aflags", "am", "android.software.credentials.prebuilt.xml", // generic_system "android.software.webview.prebuilt.xml", // media_system "android.software.window_magnification.prebuilt.xml", // handheld_system "android.system.suspend-service", "apexd", "appops", "approved-ogki-builds.xml", // base_system "appwidget", "atrace", "audioserver", "bcc", "blank_screen", "blkid", "bmgr", "bootanimation", "bootstat", "bpfloader", "bu", "bugreport", "bugreportz", "cameraserver", "cgroups.json", "cmd", "content", "cppreopts.sh", // generic_system "credstore", "debuggerd", "device_config", "dirty-image-objects", "dmctl", "dmesgd", "dnsmasq", "dpm", "dump.erofs", "dumpstate", "dumpsys", "e2fsck", "enhanced-confirmation.xml", // base_system "etc_hosts", "flags_health_check", "framework-audio_effects.xml", // for handheld // handheld_system "framework-sysconfig.xml", "fs_config_dirs_system", "fs_config_files_system", "fsck.erofs", "fsck.f2fs", // for media_system "fsck_msdos", "fsverity-release-cert-der", "gatekeeperd", "gpu_counter_producer", "gpuservice", "group_system", "gsi_tool", "gsid", "heapprofd", "hid", "hiddenapi-package-whitelist.xml", // from runtime_libart "idc_data", "idmap2", "idmap2d", "ime", "incident", "incident-helper-cmd", "incident_helper", "incidentd", "init.environ.rc-soong", "init.usb.configfs.rc", "init.usb.rc", "init.zygote32.rc", "init.zygote64.rc", "init.zygote64_32.rc", "initial-package-stopped-states.xml", "input", "installd", "ip", // base_system "iptables", "kcmdlinectrl", "kernel-lifetimes.xml", // base_system "keychars_data", "keylayout_data", "keystore2", "ld.mc", "llkd", // base_system "lmkd", // base_system "locksettings", // base_system "logcat", // base_system "logd", // base_system "lpdump", // base_system "lshal", // base_system "make_f2fs", // media_system "mdnsd", // base_system "media_profiles_V1_0.dtd", // base_system "mediacodec.policy", // base_system "mediaextractor", // base_system "mediametrics", // base_system "misctrl", // from base_system "mke2fs", // base_system "mkfs.erofs", // base_system "monkey", // base_system "mtectrl", // base_system "ndc", // base_system "netd", // base_system "netutils-wrapper-1.0", // full_base "notice_xml_system", "odsign", // base_system "otapreopt_script", // generic_system "package-shareduid-allowlist.xml", // base_system "passwd_system", // base_system "pbtombstone", // base_system "perfetto", // base_system "ping", // base_system "ping6", // base_system "pintool", // base_system "platform.xml", // base_system "pm", // base_system "prefetch", //base_system "preinstalled-packages-asl-files.xml", // base_system "preinstalled-packages-platform-generic-system.xml", // generic_system "preinstalled-packages-platform-handheld-system.xml", // handheld_system "preinstalled-packages-platform.xml", // base_system "preinstalled-packages-strict-signature.xml", // base_system "preloaded-classes", // ok "privapp-permissions-platform.xml", // base_system "prng_seeder", // base_system "public.libraries.android.txt", "recovery-persist", // base_system "recovery-refresh", // generic_system "requestsync", // media_system "resize2fs", // base_system "rss_hwm_reset", // base_system "run-as", // base_system "schedtest", // base_system "screencap", // base_system "screenrecord", // handheld_system "sdcard", // base_system "secdiscard", // base_system "sensorservice", // base_system "service", // base_system "servicemanager", // base_system "settings", // base_system "sfdo", // base_system "sgdisk", // base_system "sm", // base_system "snapuserd", // base_system "storaged", // base_system "surfaceflinger", // base_system "svc", // base_system "system_manifest.xml", // base_system "task_profiles.json", // base_system "tc", // base_system "telecom", // base_system "tombstoned", // base_system "traced", // base_system "traced_probes", // base_system "tradeinmode", // base_system "tune2fs", // base_system "uiautomator", // base_system "uinput", // base_system "uncrypt", // base_system "update_engine", // generic_system "update_engine_sideload", // recovery "update_verifier", // generic_system "usbd", // base_system "vdc", // base_system "virtual_camera", // handheld_system // release_package_virtual_camera "vold", // base_system "vr", // handheld_system "watchdogd", // base_system "wifi.rc", // base_system "wificond", // base_system "wm", // base_system ] + select(release_flag("RELEASE_PLATFORM_VERSION_CODENAME"), { "REL": [], default: [ "android.software.preview_sdk.prebuilt.xml", // media_system ], }) + select(release_flag("RELEASE_MEMORY_MANAGEMENT_DAEMON"), { true: [ "mm_daemon", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON) ], default: [ "init-mmd-prop.rc", // base_system ], }) + select(product_variable("debuggable"), { true: [ "alloctop", "adevice_fingerprint", "arping", "avbctl", "bootctl", "dmuserd", "evemu-record", "idlcli", "init-debug.rc", "iotop", "iperf3", "iw", "layertracegenerator", "logpersist.start", "logtagd.rc", "ot-cli-ftd", "ot-ctl", "overlay_remounter", "procrank", "profcollectctl", "profcollectd", "record_binder", "sanitizer-status", "servicedispatcher", "showmap", "snapshotctl", "sqlite3", "ss", "start_with_lockagent", "strace", "su", "tinycap", "tinyhostless", "tinymix", "tinypcminfo", "tinyplay", // host "tracepath", "tracepath6", "traceroute6", "unwind_info", "unwind_reg_info", "unwind_symbols", "update_engine_client", ], default: [], }) + select(release_flag("RELEASE_UPROBESTATS_MODULE"), { true: [], default: [ "uprobestats", // base_system internal ], }), multilib: { common: { deps: [ "BackupRestoreConfirmation", // base_system "BasicDreams", // handheld_system "BlockedNumberProvider", // handheld_system "BluetoothMidiService", // handheld_system "BookmarkProvider", // handheld_system "BuiltInPrintService", // handheld_system "CalendarProvider", // handheld_system "CallLogBackup", // telephony_system "CameraExtensionsProxy", // handheld_system "CaptivePortalLogin", // handheld_system "CarrierDefaultApp", // telephony_system "CellBroadcastLegacyApp", // telephony_system "CertInstaller", // handheld_system "CompanionDeviceManager", // media_system "ContactsProvider", // base_system "CredentialManager", // handheld_system "DeviceAsWebcam", // handheld_system "DeviceDiagnostics", // handheld_system - internal "DocumentsUI", // handheld_system "DownloadProvider", // base_system "DownloadProviderUi", // handheld_system "DynamicSystemInstallationService", // base_system "E2eeContactKeysProvider", // base_system "EasterEgg", // handheld_system "ExtShared", // base_system "ExternalStorageProvider", // handheld_system "FusedLocation", // handheld_system "HTMLViewer", // media_system "InputDevices", // handheld_system "IntentResolver", // base_system "KeyChain", // handheld_system "LiveWallpapersPicker", // generic_system, full_base "LocalTransport", // base_system "ManagedProvisioning", // handheld_system "MediaProviderLegacy", // base_system "MmsService", // handheld_system "MtpService", // handheld_system "MusicFX", // handheld_system "NetworkStack", // base_system "ONS", // telephony_system "PacProcessor", // handheld_system "PackageInstaller", // base_system "PartnerBookmarksProvider", // generic_system "PrintRecommendationService", // handheld_system "PrintSpooler", // handheld_system "ProxyHandler", // handheld_system "SecureElement", // handheld_system "SettingsProvider", // base_system "SharedStorageBackup", // handheld_system "Shell", // base_system "SimAppDialog", // handheld_system "SoundPicker", // not installed by anyone "Stk", // generic_system "Tag", // generic_system "TeleService", // handheld_system "Telecom", // handheld_system "TelephonyProvider", // handheld_system "Traceur", // handheld_system "UserDictionaryProvider", // handheld_system "VpnDialogs", // handheld_system "WallpaperBackup", // base_system "adbd_system_api", // base_system "android.hidl.base-V1.0-java", // base_system "android.hidl.manager-V1.0-java", // base_system "android.test.base", // from runtime_libart "android.test.mock", // base_system "android.test.runner", // base_system "aosp_mainline_modules", // ok "build_flag_system", // base_system "charger_res_images", // generic_system "com.android.apex.cts.shim.v1_prebuilt", // ok "com.android.cellbroadcast", // telephony_system "com.android.future.usb.accessory", // media_system "com.android.location.provider", // base_system "com.android.media.remotedisplay", // media_system "com.android.media.remotedisplay.xml", // media_system "com.android.mediadrm.signer", // media_system "com.android.nfc_extras", // ok "com.android.nfcservices", // base_system (RELEASE_PACKAGE_NFC_STACK != NfcNci) "com.android.runtime", // ok "dex_bootjars", "ext", // from runtime_libart "framework-graphics", // base_system "framework-location", // base_system "framework-minus-apex-install-dependencies", // base_system "framework_compatibility_matrix.device.xml", "generic_system_fonts", // ok "hwservicemanager_compat_symlink_module", // base_system "hyph-data", "ims-common", // base_system "init_system", // base_system "javax.obex", // base_system "llndk.libraries.txt", //ok "org.apache.http.legacy", // base_system "perfetto-extras", // system "sanitizer.libraries.txt", // base_system "selinux_policy_system_soong", // ok "services", // base_system "shell_and_utilities_system", // ok "system-build.prop", "system_compatibility_matrix.xml", //base_system "telephony-common", // libs from TeleService "voip-common", // base_system ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), { "true": [ "com.android.crashrecovery", // base_system (RELEASE_CRASHRECOVERY_MODULE) ], default: [ "framework-platformcrashrecovery", // base_system ], }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), { true: [ "com.android.neuralnetworks", // base_system (RELEASE_ONDEVICE_INTELLIGENCE_MODULE) ], default: [ "framework-ondeviceintelligence-platform", // base_system ], }) + select(soong_config_variable("ANDROID", "release_package_profiling_module"), { "true": [ "com.android.profiling", // base_system (RELEASE_PACKAGE_PROFILING_MODULE) ], default: [], }) + select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), { true: [], default: [ "framework-connectivity-b", // base_system ], }) + select(release_flag("RELEASE_UPROBESTATS_MODULE"), { true: [ "com.android.uprobestats", // base_system (RELEASE_UPROBESTATS_MODULE) ], default: [], }), }, prefer32: { deps: [ "drmserver", // media_system "mediaserver", // base_system ], }, lib64: { deps: [ "android.system.virtualizationcommon-ndk", "android.system.virtualizationservice-ndk", "libgsi", "servicemanager", ], }, both: { deps: [ "android.hardware.biometrics.fingerprint@2.1", // generic_system "android.hardware.radio.config@1.0", // generic_system "android.hardware.radio.deprecated@1.0", // generic_system "android.hardware.radio@1.0", // generic_system "android.hardware.radio@1.1", // generic_system "android.hardware.radio@1.2", // generic_system "android.hardware.radio@1.3", // generic_system "android.hardware.radio@1.4", // generic_system "android.hardware.secure_element@1.0", // generic_system "app_process", // base_system "boringssl_self_test", // base_system "heapprofd_client", // base_system "libEGL", // base_system "libEGL_angle", // base_system "libETC1", // base_system "libFFTEm", // base_system "libGLESv1_CM", // base_system "libGLESv1_CM_angle", // base_system "libGLESv2", // base_system "libGLESv2_angle", // base_system "libGLESv3", // base_system "libOpenMAXAL", // base_system "libOpenSLES", // base_system "libaaudio", // base_system "libalarm_jni", // base_system "libamidi", // base_system "libandroid", "libandroid_runtime", "libandroid_servers", "libandroidfw", "libartpalette-system", "libaudio-resampler", // generic-system "libaudioeffect_jni", "libaudiohal", // generic-system "libaudiopolicyengineconfigurable", // generic-system "libbinder", "libbinder_ndk", "libbinder_rpc_unstable", "libcamera2ndk", "libcgrouprc", // llndk library "libclang_rt.asan", "libcompiler_rt", "libcutils", // used by many libs "libdmabufheap", // used by many libs "libdrm", // used by many libs // generic_system "libdrmframework", // base_system "libdrmframework_jni", // base_system "libfdtrack", // base_system "libfilterfw", // base_system "libfilterpack_imageproc", // media_system "libfwdlockengine", // generic_system "libgatekeeper", // base_system "libgui", // base_system "libhardware", // base_system "libhardware_legacy", // base_system "libhidltransport", // generic_system "libhwbinder", // generic_system "libinput", // base_system "libinputflinger", // base_system "libiprouteutil", // base_system "libjnigraphics", // base_system "libjpeg", // base_system "liblog", // base_system "liblogwrap", // generic_system "liblz4", // generic_system "libmedia", // base_system "libmedia_jni", // base_system "libmediandk", // base_system "libminui", // generic_system "libmonkey_jni", // base_system - internal "libmtp", // base_system "libnetd_client", // base_system "libnetlink", // base_system "libnetutils", // base_system "libneuralnetworks_packageinfo", // base_system "libnl", // generic_system "libpdfium", // base_system "libpolicy-subsystem", // generic_system "libpower", // base_system "libpowermanager", // base_system "libprotobuf-cpp-full", // generic_system "libradio_metadata", // base_system "librs_jni", // handheld_system "librtp_jni", // base_system "libsensorservice", // base_system "libsfplugin_ccodec", // base_system "libskia", // base_system "libsonic", // base_system "libsonivox", // base_system "libsoundpool", // base_system "libspeexresampler", // base_system "libsqlite", // base_system "libstagefright", // base_system "libstagefright_foundation", // base_system "libstagefright_omx", // base_system "libstdc++", // base_system "libsysutils", // base_system "libui", // base_system "libusbhost", // base_system "libutils", // base_system "libvendorsupport", // llndk library "libvintf_jni", // base_system "libvulkan", // base_system "libwebviewchromium_loader", // media_system "libwebviewchromium_plat_support", // media_system "libwilhelm", // base_system "linker", // base_system ] + select(soong_config_variable("ANDROID", "TARGET_DYNAMIC_64_32_DRMSERVER"), { "true": ["drmserver"], default: [], }) + select(soong_config_variable("ANDROID", "TARGET_DYNAMIC_64_32_MEDIASERVER"), { "true": ["mediaserver"], default: [], }) + select(release_flag("RELEASE_UPROBESTATS_MODULE"), { true: [], default: [ "libuprobestats_client", // base_system internal ], }), }, }, arch: { arm64: { deps: [ "libclang_rt.hwasan", "libc_hwasan", ], }, }, } android_system_image { name: "aosp_shared_system_image", defaults: ["system_image_defaults"], dirs: android_rootdirs, symlinks: android_symlinks, type: "erofs", erofs: { compressor: "lz4hc,9", compress_hints: "erofs_compress_hints.txt", }, deps: [ // DO NOT update this list. Instead, update the system_image_defaults to // sync with the base_system.mk "logpersist.start", // cf only ], } ================================================ FILE: target/product/generic/OWNERS ================================================ # Bug component: 1322713 inseob@google.com jeongik@google.com jiyong@google.com justinyun@google.com kiyoungkim@google.com ================================================ FILE: target/product/generic/erofs_compress_hints.txt ================================================ 0 .*\.apex$ ================================================ FILE: target/product/generic.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a generic phone product that isn't specialized for a specific device. # It includes the base Android platform. $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk) # Overrides PRODUCT_BRAND := generic PRODUCT_DEVICE := generic PRODUCT_NAME := generic allowed_list := product_manifest.xml # TODO(b/182105280): When ART prebuilts are used in this product, Soong doesn't # produce any Android.mk entries for them. Exclude them until that problem is # fixed. allowed_list += com.android.art com.android.art.debug $(call enforce-product-packages-exist,$(allowed_list)) ================================================ FILE: target/product/generic_no_telephony.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This product is a generic phone or tablet, that doesn't have telephony. # # Note: Do not add any contents directly to this file. Choose either # handheld_system or handheld_vendor depending on partition (also consider # base_.mk or media_.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk) PRODUCT_BRAND := generic PRODUCT_DEVICE := generic PRODUCT_NAME := generic_no_telephony ================================================ FILE: target/product/generic_ramdisk.mk ================================================ # # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile installs contents of the generic ramdisk. # Inherit from this makefile to declare that this product uses generic ramdisk. # This makefile checks that other makefiles must not install things to the # ramdisk. # Ramdisk PRODUCT_PACKAGES += \ init_first_stage \ snapuserd_ramdisk \ ramdisk-build.prop \ toolbox_ramdisk \ # Debug ramdisk PRODUCT_PACKAGES += \ adb_debug.prop \ userdebug_plat_sepolicy.cil \ # For targets using dedicated recovery partition, generic ramdisk # might be relocated to recovery partition _my_paths := \ $(TARGET_COPY_OUT_RAMDISK)/ \ $(TARGET_COPY_OUT_DEBUG_RAMDISK)/ \ $(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/system \ # We use the "relaxed" version here because tzdata / tz_version is only produced # by this makefile on a subset of devices. # TODO: remove this $(call require-artifacts-in-path-relaxed, $(_my_paths), ) ================================================ FILE: target/product/generic_system.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile is the basis of a generic system image for a handheld device. $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) # Add adb keys to debuggable AOSP builds (if they exist) $(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk) # Enable updating of APEXes $(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) # Shared java libs PRODUCT_PACKAGES += \ com.android.nfc_extras \ # Applications PRODUCT_PACKAGES += \ LiveWallpapersPicker \ PartnerBookmarksProvider \ preinstalled-packages-platform-generic-system.xml \ Stk \ Tag \ # OTA support PRODUCT_PACKAGES += \ recovery-refresh \ update_engine \ update_verifier \ # Wrapped net utils for /vendor access. PRODUCT_PACKAGES += netutils-wrapper-1.0 # Charger images PRODUCT_PACKAGES += charger_res_images # system_other support PRODUCT_PACKAGES += \ cppreopts.sh \ otapreopt_script \ # For ringtones that rely on forward lock encryption PRODUCT_PACKAGES += libfwdlockengine # System libraries commonly depended on by things on the system_ext or product partitions. # These lists will be pruned periodically. PRODUCT_PACKAGES += \ android.hardware.biometrics.fingerprint@2.1 \ android.hardware.radio@1.0 \ android.hardware.radio@1.1 \ android.hardware.radio@1.2 \ android.hardware.radio@1.3 \ android.hardware.radio@1.4 \ android.hardware.radio.config@1.0 \ android.hardware.radio.deprecated@1.0 \ android.hardware.secure_element@1.0 \ libaudio-resampler \ libaudiohal \ libdrm \ liblogwrap \ liblz4 \ libminui \ libnl \ libprotobuf-cpp-full \ # These libraries are empty and have been combined into libhidlbase, but are still depended # on by things off /system. # TODO(b/135686713): remove these PRODUCT_PACKAGES += \ libhidltransport \ libhwbinder \ PRODUCT_PACKAGES_DEBUG += \ avbctl \ bootctl \ tinycap \ tinyhostless \ tinymix \ tinypcminfo \ tinyplay \ update_engine_client \ PRODUCT_HOST_PACKAGES += \ tinyplay # Enable configurable audio policy PRODUCT_PACKAGES += \ libaudiopolicyengineconfigurable \ libpolicy-subsystem # Add all of the packages used to support older/upgrading devices # These can be removed as we drop support for the older API levels PRODUCT_PACKAGES += \ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29) \ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33) \ $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34) # Include all zygote init scripts. "ro.zygote" will select one of them. PRODUCT_PACKAGES += \ init.zygote32.rc \ init.zygote64.rc \ init.zygote64_32.rc # Support Credential Manager PRODUCT_PACKAGES += \ android.software.credentials.prebuilt.xml # Enable dynamic partition size PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true PRODUCT_ENFORCE_RRO_TARGETS := * PRODUCT_NAME := generic_system PRODUCT_BRAND := generic # Define /system partition-specific product properties to identify that /system # partition is generic_system. PRODUCT_SYSTEM_NAME := mainline PRODUCT_SYSTEM_BRAND := Android PRODUCT_SYSTEM_MANUFACTURER := Android PRODUCT_SYSTEM_MODEL := mainline PRODUCT_SYSTEM_DEVICE := generic _base_mk_allowed_list := _my_allowed_list := $(_base_mk_allowed_list) # For mainline, system.img should be mounted at /, so we include ROOT here. _my_paths := \ $(TARGET_COPY_OUT_ROOT)/ \ $(TARGET_COPY_OUT_SYSTEM)/ \ $(call require-artifacts-in-path, $(_my_paths), $(_my_allowed_list)) # Product config map to toggle between sources and prebuilts of required mainline modules PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline/required/release_config_map.textproto) PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline/required/release_config_map.textproto) ================================================ FILE: target/product/generic_system_arm64.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) $(call enforce-product-packages-exist,) # Enable mainline checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true PRODUCT_BUILD_CACHE_IMAGE := false PRODUCT_BUILD_ODM_IMAGE := false PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false PRODUCT_BUILD_ODM_DLKM_IMAGE := false PRODUCT_BUILD_PRODUCT_IMAGE := false PRODUCT_BUILD_RAMDISK_IMAGE := false PRODUCT_BUILD_SYSTEM_IMAGE := true PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false PRODUCT_BUILD_USERDATA_IMAGE := false PRODUCT_BUILD_VENDOR_IMAGE := false PRODUCT_SHIPPING_API_LEVEL := 29 PRODUCT_RESTRICT_VENDOR_FILES := all PRODUCT_NAME := generic_system_arm64 PRODUCT_DEVICE := mainline_arm64 PRODUCT_BRAND := generic ================================================ FILE: target/product/generic_system_x86.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) $(call enforce-product-packages-exist,) # Enable mainline checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true PRODUCT_BUILD_CACHE_IMAGE := false PRODUCT_BUILD_ODM_IMAGE := false PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false PRODUCT_BUILD_ODM_DLKM_IMAGE := false PRODUCT_BUILD_PRODUCT_IMAGE := false PRODUCT_BUILD_RAMDISK_IMAGE := false PRODUCT_BUILD_SYSTEM_IMAGE := true PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false PRODUCT_BUILD_USERDATA_IMAGE := false PRODUCT_BUILD_VENDOR_IMAGE := false PRODUCT_SHIPPING_API_LEVEL := 29 PRODUCT_RESTRICT_VENDOR_FILES := all PRODUCT_NAME := generic_system_x86 PRODUCT_DEVICE := mainline_x86 PRODUCT_BRAND := generic ================================================ FILE: target/product/generic_system_x86_64.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) $(call enforce-product-packages-exist,) # Enable mainline checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true PRODUCT_BUILD_CACHE_IMAGE := false PRODUCT_BUILD_ODM_IMAGE := false PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false PRODUCT_BUILD_ODM_DLKM_IMAGE := false PRODUCT_BUILD_PRODUCT_IMAGE := false PRODUCT_BUILD_RAMDISK_IMAGE := false PRODUCT_BUILD_SYSTEM_IMAGE := true PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false PRODUCT_BUILD_USERDATA_IMAGE := false PRODUCT_BUILD_VENDOR_IMAGE := false PRODUCT_SHIPPING_API_LEVEL := 29 PRODUCT_RESTRICT_VENDOR_FILES := all PRODUCT_NAME := generic_system_x86_64 PRODUCT_DEVICE := mainline_x86_64 PRODUCT_BRAND := generic ================================================ FILE: target/product/generic_system_x86_arm.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # All components inherited here go to system image # $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk) $(call enforce-product-packages-exist,) # Enable mainline checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := true PRODUCT_BUILD_CACHE_IMAGE := false PRODUCT_BUILD_ODM_IMAGE := false PRODUCT_BUILD_VENDOR_DLKM_IMAGE := false PRODUCT_BUILD_ODM_DLKM_IMAGE := false PRODUCT_BUILD_PRODUCT_IMAGE := false PRODUCT_BUILD_RAMDISK_IMAGE := false PRODUCT_BUILD_SYSTEM_IMAGE := true PRODUCT_BUILD_SYSTEM_EXT_IMAGE := false PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false PRODUCT_BUILD_USERDATA_IMAGE := false PRODUCT_BUILD_VENDOR_IMAGE := false PRODUCT_SHIPPING_API_LEVEL := 29 PRODUCT_RESTRICT_VENDOR_FILES := all PRODUCT_NAME := generic_system_x86_arm PRODUCT_DEVICE := mainline_x86_arm PRODUCT_BRAND := generic ================================================ FILE: target/product/generic_x86.mk ================================================ # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a generic phone product that isn't specialized for a specific device. # It includes the base Android platform. $(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk) # Overrides PRODUCT_BRAND := generic_x86 PRODUCT_DEVICE := generic_x86 PRODUCT_NAME := generic_x86 ================================================ FILE: target/product/go_defaults.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Inherit common Android Go defaults. $(call inherit-product, build/make/target/product/go_defaults_common.mk) # Product config map to toggle between sources and prebuilts of required mainline modules PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard build/release/gms_mainline_go/required/release_config_map.textproto) PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/gms_mainline_go/required/release_config_map.textproto) # Add the system properties. TARGET_SYSTEM_PROP += \ build/make/target/board/go_defaults.prop ================================================ FILE: target/product/go_defaults_512.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Inherit common Android Go defaults. $(call inherit-product, build/make/target/product/go_defaults_common.mk) # Add the system properties. TARGET_SYSTEM_PROP += \ build/make/target/board/go_defaults_512.prop ================================================ FILE: target/product/go_defaults_common.mk ================================================ # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Sets Android Go recommended default product options.. # Set lowram options and enable traced by default PRODUCT_VENDOR_PROPERTIES += \ ro.config.low_ram=true \ # Speed profile services and wifi-service to reduce RAM and storage. PRODUCT_SYSTEM_SERVER_COMPILER_FILTER := speed-profile # Do not generate libartd. PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false # Strip the local variable table and the local variable type table to reduce # the size of the system image. This has no bearing on stack traces, but will # leave less information available via JDWP. PRODUCT_MINIMIZE_JAVA_DEBUG_INFO := true # Use the low memory allocator outside of eng builds to save RSS. ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT))) MALLOC_LOW_MEMORY := true endif # Add the system properties. TARGET_SYSTEM_PROP += \ build/make/target/board/go_defaults_common.prop # use the go specific handheld_core_hardware.xml from frameworks PRODUCT_COPY_FILES += \ frameworks/native/data/etc/go_handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml ================================================ FILE: target/product/gsi/28.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libhidltransport.so VNDK-SP: libhwbinder.so VNDK-SP: libhwbinder_noltopgo.so VNDK-SP: libion.so VNDK-SP: liblzma.so VNDK-SP: libunwind.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.frameworks.displayservice@1.0.so VNDK-core: android.frameworks.schedulerservice@1.0.so VNDK-core: android.frameworks.sensorservice@1.0.so VNDK-core: android.frameworks.vr.composer@1.0.so VNDK-core: android.hardware.audio.common-util.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.audio.common@2.0-util.so VNDK-core: android.hardware.audio.common@4.0.so VNDK-core: android.hardware.audio.common@4.0-util.so VNDK-core: android.hardware.audio.effect@2.0.so VNDK-core: android.hardware.audio.effect@4.0.so VNDK-core: android.hardware.audio@2.0.so VNDK-core: android.hardware.audio@4.0.so VNDK-core: android.hardware.authsecret@1.0.so VNDK-core: android.hardware.automotive.audiocontrol@1.0.so VNDK-core: android.hardware.automotive.evs@1.0.so VNDK-core: android.hardware.automotive.vehicle@2.0.so VNDK-core: android.hardware.biometrics.fingerprint@2.1.so VNDK-core: android.hardware.bluetooth.a2dp@1.0.so VNDK-core: android.hardware.bluetooth@1.0.so VNDK-core: android.hardware.boot@1.0.so VNDK-core: android.hardware.broadcastradio@1.0.so VNDK-core: android.hardware.broadcastradio@1.1.so VNDK-core: android.hardware.broadcastradio@2.0.so VNDK-core: android.hardware.camera.common@1.0.so VNDK-core: android.hardware.camera.device@1.0.so VNDK-core: android.hardware.camera.device@3.2.so VNDK-core: android.hardware.camera.device@3.3.so VNDK-core: android.hardware.camera.device@3.4.so VNDK-core: android.hardware.camera.metadata@3.2.so VNDK-core: android.hardware.camera.metadata@3.3.so VNDK-core: android.hardware.camera.provider@2.4.so VNDK-core: android.hardware.cas.native@1.0.so VNDK-core: android.hardware.cas@1.0.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.confirmationui@1.0.so VNDK-core: android.hardware.contexthub@1.0.so VNDK-core: android.hardware.drm@1.0.so VNDK-core: android.hardware.drm@1.1.so VNDK-core: android.hardware.dumpstate@1.0.so VNDK-core: android.hardware.gatekeeper@1.0.so VNDK-core: android.hardware.gnss@1.0.so VNDK-core: android.hardware.gnss@1.1.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.composer@2.1.so VNDK-core: android.hardware.graphics.composer@2.2.so VNDK-core: android.hardware.health@1.0.so VNDK-core: android.hardware.health@2.0.so VNDK-core: android.hardware.ir@1.0.so VNDK-core: android.hardware.keymaster@3.0.so VNDK-core: android.hardware.keymaster@4.0.so VNDK-core: android.hardware.light@2.0.so VNDK-core: android.hardware.media.bufferpool@1.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.neuralnetworks@1.0.so VNDK-core: android.hardware.neuralnetworks@1.1.so VNDK-core: android.hardware.nfc@1.0.so VNDK-core: android.hardware.nfc@1.1.so VNDK-core: android.hardware.oemlock@1.0.so VNDK-core: android.hardware.power@1.0.so VNDK-core: android.hardware.power@1.1.so VNDK-core: android.hardware.power@1.2.so VNDK-core: android.hardware.radio.config@1.0.so VNDK-core: android.hardware.radio.deprecated@1.0.so VNDK-core: android.hardware.radio@1.0.so VNDK-core: android.hardware.radio@1.1.so VNDK-core: android.hardware.radio@1.2.so VNDK-core: android.hardware.secure_element@1.0.so VNDK-core: android.hardware.sensors@1.0.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.1.so VNDK-core: android.hardware.tetheroffload.config@1.0.so VNDK-core: android.hardware.tetheroffload.control@1.0.so VNDK-core: android.hardware.thermal@1.0.so VNDK-core: android.hardware.thermal@1.1.so VNDK-core: android.hardware.tv.cec@1.0.so VNDK-core: android.hardware.tv.input@1.0.so VNDK-core: android.hardware.usb.gadget@1.0.so VNDK-core: android.hardware.usb@1.0.so VNDK-core: android.hardware.usb@1.1.so VNDK-core: android.hardware.vibrator@1.0.so VNDK-core: android.hardware.vibrator@1.1.so VNDK-core: android.hardware.vibrator@1.2.so VNDK-core: android.hardware.vr@1.0.so VNDK-core: android.hardware.weaver@1.0.so VNDK-core: android.hardware.wifi.hostapd@1.0.so VNDK-core: android.hardware.wifi.offload@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.1.so VNDK-core: android.hardware.wifi@1.0.so VNDK-core: android.hardware.wifi@1.1.so VNDK-core: android.hardware.wifi@1.2.so VNDK-core: android.hidl.allocator@1.0.so VNDK-core: android.hidl.memory.block@1.0.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.system.net.netd@1.0.so VNDK-core: android.system.net.netd@1.1.so VNDK-core: android.system.wifi.keystore@1.0.so VNDK-core: libadf.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libhidlcache.so VNDK-core: libjpeg.so VNDK-core: libkeymaster_messages.so VNDK-core: libkeymaster_portable.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libopus.so VNDK-core: libpagemap.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libprotobuf-cpp-full.so VNDK-core: libprotobuf-cpp-lite.so VNDK-core: libpuresoftkeymasterdevice.so VNDK-core: libradio_metadata.so VNDK-core: libselinux.so VNDK-core: libsoftkeymasterdevice.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_amrnb_common.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_enc_common.so VNDK-core: libstagefright_flacdec.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_soft_aacdec.so VNDK-core: libstagefright_soft_aacenc.so VNDK-core: libstagefright_soft_amrdec.so VNDK-core: libstagefright_soft_amrnbenc.so VNDK-core: libstagefright_soft_amrwbenc.so VNDK-core: libstagefright_soft_avcdec.so VNDK-core: libstagefright_soft_avcenc.so VNDK-core: libstagefright_soft_flacdec.so VNDK-core: libstagefright_soft_flacenc.so VNDK-core: libstagefright_soft_g711dec.so VNDK-core: libstagefright_soft_gsmdec.so VNDK-core: libstagefright_soft_hevcdec.so VNDK-core: libstagefright_soft_mp3dec.so VNDK-core: libstagefright_soft_mpeg2dec.so VNDK-core: libstagefright_soft_mpeg4dec.so VNDK-core: libstagefright_soft_mpeg4enc.so VNDK-core: libstagefright_soft_opusdec.so VNDK-core: libstagefright_soft_rawdec.so VNDK-core: libstagefright_soft_vorbisdec.so VNDK-core: libstagefright_soft_vpxdec.so VNDK-core: libstagefright_soft_vpxenc.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsuspend.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libvixl-arm.so VNDK-core: libvixl-arm64.so VNDK-core: libvorbisidec.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-private: libunwind.so ================================================ FILE: target/product/gsi/29.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libbinderthreadstate.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libhidltransport.so VNDK-SP: libhwbinder.so VNDK-SP: libhwbinder_noltopgo.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.frameworks.cameraservice.common@2.0.so VNDK-core: android.frameworks.cameraservice.device@2.0.so VNDK-core: android.frameworks.cameraservice.service@2.0.so VNDK-core: android.frameworks.displayservice@1.0.so VNDK-core: android.frameworks.schedulerservice@1.0.so VNDK-core: android.frameworks.sensorservice@1.0.so VNDK-core: android.frameworks.stats@1.0.so VNDK-core: android.frameworks.vr.composer@1.0.so VNDK-core: android.hardware.atrace@1.0.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.audio.common@4.0.so VNDK-core: android.hardware.audio.common@5.0.so VNDK-core: android.hardware.audio.effect@2.0.so VNDK-core: android.hardware.audio.effect@4.0.so VNDK-core: android.hardware.audio.effect@5.0.so VNDK-core: android.hardware.audio@2.0.so VNDK-core: android.hardware.audio@4.0.so VNDK-core: android.hardware.audio@5.0.so VNDK-core: android.hardware.authsecret@1.0.so VNDK-core: android.hardware.automotive.audiocontrol@1.0.so VNDK-core: android.hardware.automotive.evs@1.0.so VNDK-core: android.hardware.automotive.vehicle@2.0.so VNDK-core: android.hardware.biometrics.face@1.0.so VNDK-core: android.hardware.biometrics.fingerprint@2.1.so VNDK-core: android.hardware.bluetooth.a2dp@1.0.so VNDK-core: android.hardware.bluetooth.audio@2.0.so VNDK-core: android.hardware.bluetooth@1.0.so VNDK-core: android.hardware.boot@1.0.so VNDK-core: android.hardware.broadcastradio@1.0.so VNDK-core: android.hardware.broadcastradio@1.1.so VNDK-core: android.hardware.broadcastradio@2.0.so VNDK-core: android.hardware.camera.common@1.0.so VNDK-core: android.hardware.camera.device@1.0.so VNDK-core: android.hardware.camera.device@3.2.so VNDK-core: android.hardware.camera.device@3.3.so VNDK-core: android.hardware.camera.device@3.4.so VNDK-core: android.hardware.camera.device@3.5.so VNDK-core: android.hardware.camera.metadata@3.2.so VNDK-core: android.hardware.camera.metadata@3.3.so VNDK-core: android.hardware.camera.metadata@3.4.so VNDK-core: android.hardware.camera.provider@2.4.so VNDK-core: android.hardware.camera.provider@2.5.so VNDK-core: android.hardware.cas.native@1.0.so VNDK-core: android.hardware.cas@1.0.so VNDK-core: android.hardware.cas@1.1.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.confirmationui@1.0.so VNDK-core: android.hardware.contexthub@1.0.so VNDK-core: android.hardware.drm@1.0.so VNDK-core: android.hardware.drm@1.1.so VNDK-core: android.hardware.drm@1.2.so VNDK-core: android.hardware.dumpstate@1.0.so VNDK-core: android.hardware.fastboot@1.0.so VNDK-core: android.hardware.gatekeeper@1.0.so VNDK-core: android.hardware.gnss.measurement_corrections@1.0.so VNDK-core: android.hardware.gnss.visibility_control@1.0.so VNDK-core: android.hardware.gnss@1.0.so VNDK-core: android.hardware.gnss@1.1.so VNDK-core: android.hardware.gnss@2.0.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.graphics.composer@2.1.so VNDK-core: android.hardware.graphics.composer@2.2.so VNDK-core: android.hardware.graphics.composer@2.3.so VNDK-core: android.hardware.health.storage@1.0.so VNDK-core: android.hardware.health@1.0.so VNDK-core: android.hardware.health@2.0.so VNDK-core: android.hardware.input.classifier@1.0.so VNDK-core: android.hardware.input.common@1.0.so VNDK-core: android.hardware.ir@1.0.so VNDK-core: android.hardware.keymaster@3.0.so VNDK-core: android.hardware.keymaster@4.0.so VNDK-core: android.hardware.light@2.0.so VNDK-core: android.hardware.media.bufferpool@1.0.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.c2@1.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.neuralnetworks@1.0.so VNDK-core: android.hardware.neuralnetworks@1.1.so VNDK-core: android.hardware.neuralnetworks@1.2.so VNDK-core: android.hardware.nfc@1.0.so VNDK-core: android.hardware.nfc@1.1.so VNDK-core: android.hardware.nfc@1.2.so VNDK-core: android.hardware.oemlock@1.0.so VNDK-core: android.hardware.power.stats@1.0.so VNDK-core: android.hardware.power@1.0.so VNDK-core: android.hardware.power@1.1.so VNDK-core: android.hardware.power@1.2.so VNDK-core: android.hardware.power@1.3.so VNDK-core: android.hardware.radio.config@1.0.so VNDK-core: android.hardware.radio.config@1.1.so VNDK-core: android.hardware.radio.config@1.2.so VNDK-core: android.hardware.radio.deprecated@1.0.so VNDK-core: android.hardware.radio@1.0.so VNDK-core: android.hardware.radio@1.1.so VNDK-core: android.hardware.radio@1.2.so VNDK-core: android.hardware.radio@1.3.so VNDK-core: android.hardware.radio@1.4.so VNDK-core: android.hardware.secure_element@1.0.so VNDK-core: android.hardware.secure_element@1.1.so VNDK-core: android.hardware.sensors@1.0.so VNDK-core: android.hardware.sensors@2.0.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.1.so VNDK-core: android.hardware.soundtrigger@2.2.so VNDK-core: android.hardware.tetheroffload.config@1.0.so VNDK-core: android.hardware.tetheroffload.control@1.0.so VNDK-core: android.hardware.thermal@1.0.so VNDK-core: android.hardware.thermal@1.1.so VNDK-core: android.hardware.thermal@2.0.so VNDK-core: android.hardware.tv.cec@1.0.so VNDK-core: android.hardware.tv.cec@2.0.so VNDK-core: android.hardware.tv.input@1.0.so VNDK-core: android.hardware.usb.gadget@1.0.so VNDK-core: android.hardware.usb@1.0.so VNDK-core: android.hardware.usb@1.1.so VNDK-core: android.hardware.usb@1.2.so VNDK-core: android.hardware.vibrator@1.0.so VNDK-core: android.hardware.vibrator@1.1.so VNDK-core: android.hardware.vibrator@1.2.so VNDK-core: android.hardware.vibrator@1.3.so VNDK-core: android.hardware.vr@1.0.so VNDK-core: android.hardware.weaver@1.0.so VNDK-core: android.hardware.wifi.hostapd@1.0.so VNDK-core: android.hardware.wifi.hostapd@1.1.so VNDK-core: android.hardware.wifi.offload@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.1.so VNDK-core: android.hardware.wifi.supplicant@1.2.so VNDK-core: android.hardware.wifi@1.0.so VNDK-core: android.hardware.wifi@1.1.so VNDK-core: android.hardware.wifi@1.2.so VNDK-core: android.hardware.wifi@1.3.so VNDK-core: android.hidl.allocator@1.0.so VNDK-core: android.hidl.memory.block@1.0.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.system.net.netd@1.0.so VNDK-core: android.system.net.netd@1.1.so VNDK-core: android.system.suspend@1.0.so VNDK-core: android.system.wifi.keystore@1.0.so VNDK-core: libadf.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libhidlcache.so VNDK-core: libjpeg.so VNDK-core: libkeymaster_messages.so VNDK-core: libkeymaster_portable.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libprotobuf-cpp-full.so VNDK-core: libprotobuf-cpp-lite.so VNDK-core: libpuresoftkeymasterdevice.so VNDK-core: libradio_metadata.so VNDK-core: libselinux.so VNDK-core: libsoftkeymasterdevice.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libbinderthreadstate.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so ================================================ FILE: target/product/gsi/30.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V1-ndk_platform.so VNDK-SP: android.hardware.graphics.common-V1-ndk_platform.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.frameworks.automotive.display@1.0.so VNDK-core: android.frameworks.cameraservice.common@2.0.so VNDK-core: android.frameworks.cameraservice.device@2.0.so VNDK-core: android.frameworks.cameraservice.service@2.0.so VNDK-core: android.frameworks.cameraservice.service@2.1.so VNDK-core: android.frameworks.displayservice@1.0.so VNDK-core: android.frameworks.schedulerservice@1.0.so VNDK-core: android.frameworks.sensorservice@1.0.so VNDK-core: android.frameworks.stats@1.0.so VNDK-core: android.hardware.atrace@1.0.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.audio.common@4.0.so VNDK-core: android.hardware.audio.common@5.0.so VNDK-core: android.hardware.audio.common@6.0.so VNDK-core: android.hardware.audio.effect@2.0.so VNDK-core: android.hardware.audio.effect@4.0.so VNDK-core: android.hardware.audio.effect@5.0.so VNDK-core: android.hardware.audio.effect@6.0.so VNDK-core: android.hardware.audio@2.0.so VNDK-core: android.hardware.audio@4.0.so VNDK-core: android.hardware.audio@5.0.so VNDK-core: android.hardware.audio@6.0.so VNDK-core: android.hardware.authsecret@1.0.so VNDK-core: android.hardware.automotive.audiocontrol@1.0.so VNDK-core: android.hardware.automotive.audiocontrol@2.0.so VNDK-core: android.hardware.automotive.can@1.0.so VNDK-core: android.hardware.automotive.evs@1.0.so VNDK-core: android.hardware.automotive.evs@1.1.so VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so VNDK-core: android.hardware.automotive.sv@1.0.so VNDK-core: android.hardware.automotive.vehicle@2.0.so VNDK-core: android.hardware.biometrics.face@1.0.so VNDK-core: android.hardware.biometrics.fingerprint@2.1.so VNDK-core: android.hardware.biometrics.fingerprint@2.2.so VNDK-core: android.hardware.bluetooth.a2dp@1.0.so VNDK-core: android.hardware.bluetooth.audio@2.0.so VNDK-core: android.hardware.bluetooth@1.0.so VNDK-core: android.hardware.bluetooth@1.1.so VNDK-core: android.hardware.boot@1.0.so VNDK-core: android.hardware.boot@1.1.so VNDK-core: android.hardware.broadcastradio@1.0.so VNDK-core: android.hardware.broadcastradio@1.1.so VNDK-core: android.hardware.broadcastradio@2.0.so VNDK-core: android.hardware.camera.common@1.0.so VNDK-core: android.hardware.camera.device@1.0.so VNDK-core: android.hardware.camera.device@3.2.so VNDK-core: android.hardware.camera.device@3.3.so VNDK-core: android.hardware.camera.device@3.4.so VNDK-core: android.hardware.camera.device@3.5.so VNDK-core: android.hardware.camera.device@3.6.so VNDK-core: android.hardware.camera.metadata@3.2.so VNDK-core: android.hardware.camera.metadata@3.3.so VNDK-core: android.hardware.camera.metadata@3.4.so VNDK-core: android.hardware.camera.metadata@3.5.so VNDK-core: android.hardware.camera.provider@2.4.so VNDK-core: android.hardware.camera.provider@2.5.so VNDK-core: android.hardware.camera.provider@2.6.so VNDK-core: android.hardware.cas.native@1.0.so VNDK-core: android.hardware.cas@1.0.so VNDK-core: android.hardware.cas@1.1.so VNDK-core: android.hardware.cas@1.2.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.confirmationui@1.0.so VNDK-core: android.hardware.contexthub@1.0.so VNDK-core: android.hardware.contexthub@1.1.so VNDK-core: android.hardware.drm@1.0.so VNDK-core: android.hardware.drm@1.1.so VNDK-core: android.hardware.drm@1.2.so VNDK-core: android.hardware.drm@1.3.so VNDK-core: android.hardware.dumpstate@1.0.so VNDK-core: android.hardware.dumpstate@1.1.so VNDK-core: android.hardware.fastboot@1.0.so VNDK-core: android.hardware.gatekeeper@1.0.so VNDK-core: android.hardware.gnss.measurement_corrections@1.0.so VNDK-core: android.hardware.gnss.measurement_corrections@1.1.so VNDK-core: android.hardware.gnss.visibility_control@1.0.so VNDK-core: android.hardware.gnss@1.0.so VNDK-core: android.hardware.gnss@1.1.so VNDK-core: android.hardware.gnss@2.0.so VNDK-core: android.hardware.gnss@2.1.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.graphics.composer@2.1.so VNDK-core: android.hardware.graphics.composer@2.2.so VNDK-core: android.hardware.graphics.composer@2.3.so VNDK-core: android.hardware.graphics.composer@2.4.so VNDK-core: android.hardware.health.storage@1.0.so VNDK-core: android.hardware.health@1.0.so VNDK-core: android.hardware.health@2.0.so VNDK-core: android.hardware.health@2.1.so VNDK-core: android.hardware.identity-V2-ndk_platform.so VNDK-core: android.hardware.input.classifier@1.0.so VNDK-core: android.hardware.input.common@1.0.so VNDK-core: android.hardware.ir@1.0.so VNDK-core: android.hardware.keymaster-V2-ndk_platform.so VNDK-core: android.hardware.keymaster@3.0.so VNDK-core: android.hardware.keymaster@4.0.so VNDK-core: android.hardware.keymaster@4.1.so VNDK-core: android.hardware.light-V1-ndk_platform.so VNDK-core: android.hardware.light@2.0.so VNDK-core: android.hardware.media.bufferpool@1.0.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.c2@1.0.so VNDK-core: android.hardware.media.c2@1.1.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.neuralnetworks@1.0.so VNDK-core: android.hardware.neuralnetworks@1.1.so VNDK-core: android.hardware.neuralnetworks@1.2.so VNDK-core: android.hardware.neuralnetworks@1.3.so VNDK-core: android.hardware.nfc@1.0.so VNDK-core: android.hardware.nfc@1.1.so VNDK-core: android.hardware.nfc@1.2.so VNDK-core: android.hardware.oemlock@1.0.so VNDK-core: android.hardware.power-V1-ndk_platform.so VNDK-core: android.hardware.power.stats@1.0.so VNDK-core: android.hardware.power@1.0.so VNDK-core: android.hardware.power@1.1.so VNDK-core: android.hardware.power@1.2.so VNDK-core: android.hardware.power@1.3.so VNDK-core: android.hardware.radio.config@1.0.so VNDK-core: android.hardware.radio.config@1.1.so VNDK-core: android.hardware.radio.config@1.2.so VNDK-core: android.hardware.radio.deprecated@1.0.so VNDK-core: android.hardware.radio@1.0.so VNDK-core: android.hardware.radio@1.1.so VNDK-core: android.hardware.radio@1.2.so VNDK-core: android.hardware.radio@1.3.so VNDK-core: android.hardware.radio@1.4.so VNDK-core: android.hardware.radio@1.5.so VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so VNDK-core: android.hardware.secure_element@1.0.so VNDK-core: android.hardware.secure_element@1.1.so VNDK-core: android.hardware.secure_element@1.2.so VNDK-core: android.hardware.sensors@1.0.so VNDK-core: android.hardware.sensors@2.0.so VNDK-core: android.hardware.sensors@2.1.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.soundtrigger@2.1.so VNDK-core: android.hardware.soundtrigger@2.2.so VNDK-core: android.hardware.soundtrigger@2.3.so VNDK-core: android.hardware.tetheroffload.config@1.0.so VNDK-core: android.hardware.tetheroffload.control@1.0.so VNDK-core: android.hardware.thermal@1.0.so VNDK-core: android.hardware.thermal@1.1.so VNDK-core: android.hardware.thermal@2.0.so VNDK-core: android.hardware.tv.cec@1.0.so VNDK-core: android.hardware.tv.cec@2.0.so VNDK-core: android.hardware.tv.input@1.0.so VNDK-core: android.hardware.tv.tuner@1.0.so VNDK-core: android.hardware.usb.gadget@1.0.so VNDK-core: android.hardware.usb.gadget@1.1.so VNDK-core: android.hardware.usb@1.0.so VNDK-core: android.hardware.usb@1.1.so VNDK-core: android.hardware.usb@1.2.so VNDK-core: android.hardware.vibrator-V1-ndk_platform.so VNDK-core: android.hardware.vibrator@1.0.so VNDK-core: android.hardware.vibrator@1.1.so VNDK-core: android.hardware.vibrator@1.2.so VNDK-core: android.hardware.vibrator@1.3.so VNDK-core: android.hardware.vr@1.0.so VNDK-core: android.hardware.weaver@1.0.so VNDK-core: android.hardware.wifi.hostapd@1.0.so VNDK-core: android.hardware.wifi.hostapd@1.1.so VNDK-core: android.hardware.wifi.hostapd@1.2.so VNDK-core: android.hardware.wifi.offload@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.0.so VNDK-core: android.hardware.wifi.supplicant@1.1.so VNDK-core: android.hardware.wifi.supplicant@1.2.so VNDK-core: android.hardware.wifi.supplicant@1.3.so VNDK-core: android.hardware.wifi@1.0.so VNDK-core: android.hardware.wifi@1.1.so VNDK-core: android.hardware.wifi@1.2.so VNDK-core: android.hardware.wifi@1.3.so VNDK-core: android.hardware.wifi@1.4.so VNDK-core: android.hidl.allocator@1.0.so VNDK-core: android.hidl.memory.block@1.0.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.system.net.netd@1.0.so VNDK-core: android.system.net.netd@1.1.so VNDK-core: android.system.suspend@1.0.so VNDK-core: android.system.wifi.keystore@1.0.so VNDK-core: libadf.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so ================================================ FILE: target/product/gsi/31.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V2-ndk_platform.so VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libdmabufheap.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.authsecret-V1-ndk_platform.so VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.gnss-V1-ndk_platform.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.health.storage-V1-ndk_platform.so VNDK-core: android.hardware.identity-V3-ndk_platform.so VNDK-core: android.hardware.keymaster-V3-ndk_platform.so VNDK-core: android.hardware.light-V1-ndk_platform.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack-V1-ndk_platform.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.oemlock-V1-ndk_platform.so VNDK-core: android.hardware.power-V2-ndk_platform.so VNDK-core: android.hardware.power.stats-V1-ndk_platform.so VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.vibrator-V2-ndk_platform.so VNDK-core: android.hardware.weaver-V1-ndk_platform.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.system.keystore2-V1-ndk_platform.so VNDK-core: android.system.suspend@1.0.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-product: android.hardware.audio.common@2.0.so VNDK-product: android.hardware.configstore@1.0.so VNDK-product: android.hardware.configstore@1.1.so VNDK-product: android.hardware.graphics.allocator@2.0.so VNDK-product: android.hardware.graphics.allocator@3.0.so VNDK-product: android.hardware.graphics.allocator@4.0.so VNDK-product: android.hardware.graphics.bufferqueue@1.0.so VNDK-product: android.hardware.graphics.bufferqueue@2.0.so VNDK-product: android.hardware.graphics.common@1.0.so VNDK-product: android.hardware.graphics.common@1.1.so VNDK-product: android.hardware.graphics.common@1.2.so VNDK-product: android.hardware.graphics.mapper@2.0.so VNDK-product: android.hardware.graphics.mapper@2.1.so VNDK-product: android.hardware.graphics.mapper@3.0.so VNDK-product: android.hardware.graphics.mapper@4.0.so VNDK-product: android.hardware.media.bufferpool@2.0.so VNDK-product: android.hardware.media.omx@1.0.so VNDK-product: android.hardware.media@1.0.so VNDK-product: android.hardware.memtrack@1.0.so VNDK-product: android.hardware.renderscript@1.0.so VNDK-product: android.hardware.soundtrigger@2.0.so VNDK-product: android.hidl.memory.token@1.0.so VNDK-product: android.hidl.memory@1.0.so VNDK-product: android.hidl.safe_union@1.0.so VNDK-product: android.hidl.token@1.0.so VNDK-product: android.system.suspend@1.0.so VNDK-product: libaudioutils.so VNDK-product: libbacktrace.so VNDK-product: libbase.so VNDK-product: libc++.so VNDK-product: libcamera_metadata.so VNDK-product: libcap.so VNDK-product: libcompiler_rt.so VNDK-product: libcrypto.so VNDK-product: libcurl.so VNDK-product: libcutils.so VNDK-product: libevent.so VNDK-product: libexpat.so VNDK-product: libfmq.so VNDK-product: libhidlbase.so VNDK-product: libhidlmemory.so VNDK-product: libion.so VNDK-product: libjpeg.so VNDK-product: libjsoncpp.so VNDK-product: libldacBT_abr.so VNDK-product: libldacBT_enc.so VNDK-product: liblz4.so VNDK-product: liblzma.so VNDK-product: libminijail.so VNDK-product: libnl.so VNDK-product: libpcre2.so VNDK-product: libpiex.so VNDK-product: libpng.so VNDK-product: libprocessgroup.so VNDK-product: libprocinfo.so VNDK-product: libspeexresampler.so VNDK-product: libssl.so VNDK-product: libtinyalsa.so VNDK-product: libtinyxml2.so VNDK-product: libunwindstack.so VNDK-product: libutils.so VNDK-product: libutilscallstack.so VNDK-product: libwifi-system-iface.so VNDK-product: libxml2.so VNDK-product: libyuv.so VNDK-product: libz.so VNDK-product: libziparchive.so ================================================ FILE: target/product/gsi/32.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V2-ndk_platform.so VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libdmabufheap.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.authsecret-V1-ndk_platform.so VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.gnss-V1-ndk_platform.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.health.storage-V1-ndk_platform.so VNDK-core: android.hardware.identity-V3-ndk_platform.so VNDK-core: android.hardware.keymaster-V3-ndk_platform.so VNDK-core: android.hardware.light-V1-ndk_platform.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack-V1-ndk_platform.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.oemlock-V1-ndk_platform.so VNDK-core: android.hardware.power-V2-ndk_platform.so VNDK-core: android.hardware.power.stats-V1-ndk_platform.so VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.vibrator-V2-ndk_platform.so VNDK-core: android.hardware.weaver-V1-ndk_platform.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.system.keystore2-V1-ndk_platform.so VNDK-core: android.system.suspend@1.0.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-product: android.hardware.audio.common@2.0.so VNDK-product: android.hardware.configstore@1.0.so VNDK-product: android.hardware.configstore@1.1.so VNDK-product: android.hardware.graphics.allocator@2.0.so VNDK-product: android.hardware.graphics.allocator@3.0.so VNDK-product: android.hardware.graphics.allocator@4.0.so VNDK-product: android.hardware.graphics.bufferqueue@1.0.so VNDK-product: android.hardware.graphics.bufferqueue@2.0.so VNDK-product: android.hardware.graphics.common@1.0.so VNDK-product: android.hardware.graphics.common@1.1.so VNDK-product: android.hardware.graphics.common@1.2.so VNDK-product: android.hardware.graphics.mapper@2.0.so VNDK-product: android.hardware.graphics.mapper@2.1.so VNDK-product: android.hardware.graphics.mapper@3.0.so VNDK-product: android.hardware.graphics.mapper@4.0.so VNDK-product: android.hardware.media.bufferpool@2.0.so VNDK-product: android.hardware.media.omx@1.0.so VNDK-product: android.hardware.media@1.0.so VNDK-product: android.hardware.memtrack@1.0.so VNDK-product: android.hardware.renderscript@1.0.so VNDK-product: android.hardware.soundtrigger@2.0.so VNDK-product: android.hidl.memory.token@1.0.so VNDK-product: android.hidl.memory@1.0.so VNDK-product: android.hidl.safe_union@1.0.so VNDK-product: android.hidl.token@1.0.so VNDK-product: android.system.suspend@1.0.so VNDK-product: libaudioutils.so VNDK-product: libbacktrace.so VNDK-product: libbase.so VNDK-product: libc++.so VNDK-product: libcamera_metadata.so VNDK-product: libcap.so VNDK-product: libcompiler_rt.so VNDK-product: libcrypto.so VNDK-product: libcurl.so VNDK-product: libcutils.so VNDK-product: libevent.so VNDK-product: libexpat.so VNDK-product: libfmq.so VNDK-product: libhidlbase.so VNDK-product: libhidlmemory.so VNDK-product: libion.so VNDK-product: libjpeg.so VNDK-product: libjsoncpp.so VNDK-product: libldacBT_abr.so VNDK-product: libldacBT_enc.so VNDK-product: liblz4.so VNDK-product: liblzma.so VNDK-product: libminijail.so VNDK-product: libnl.so VNDK-product: libpcre2.so VNDK-product: libpiex.so VNDK-product: libpng.so VNDK-product: libprocessgroup.so VNDK-product: libprocinfo.so VNDK-product: libspeexresampler.so VNDK-product: libssl.so VNDK-product: libtinyalsa.so VNDK-product: libtinyxml2.so VNDK-product: libunwindstack.so VNDK-product: libutils.so VNDK-product: libutilscallstack.so VNDK-product: libwifi-system-iface.so VNDK-product: libxml2.so VNDK-product: libyuv.so VNDK-product: libz.so VNDK-product: libziparchive.so ================================================ FILE: target/product/gsi/33.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V2-ndk.so VNDK-SP: android.hardware.common.fmq-V1-ndk.so VNDK-SP: android.hardware.graphics.allocator-V1-ndk.so VNDK-SP: android.hardware.graphics.common-V3-ndk.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.composer3-V1-ndk.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbacktrace.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libdmabufheap.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.hardware.audio.common-V1-ndk.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.authsecret-V1-ndk.so VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so VNDK-core: android.hardware.bluetooth.audio-V2-ndk.so VNDK-core: android.hardware.camera.common-V1-ndk.so VNDK-core: android.hardware.camera.device-V1-ndk.so VNDK-core: android.hardware.camera.metadata-V1-ndk.so VNDK-core: android.hardware.camera.provider-V1-ndk.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.drm-V1-ndk.so VNDK-core: android.hardware.dumpstate-V1-ndk.so VNDK-core: android.hardware.gnss-V2-ndk.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.health-V1-ndk.so VNDK-core: android.hardware.health.storage-V1-ndk.so VNDK-core: android.hardware.identity-V4-ndk.so VNDK-core: android.hardware.ir-V1-ndk.so VNDK-core: android.hardware.keymaster-V3-ndk.so VNDK-core: android.hardware.light-V2-ndk.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack-V1-ndk.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.nfc-V1-ndk.so VNDK-core: android.hardware.oemlock-V1-ndk.so VNDK-core: android.hardware.power-V3-ndk.so VNDK-core: android.hardware.power.stats-V1-ndk.so VNDK-core: android.hardware.radio-V1-ndk.so VNDK-core: android.hardware.radio.config-V1-ndk.so VNDK-core: android.hardware.radio.data-V1-ndk.so VNDK-core: android.hardware.radio.messaging-V1-ndk.so VNDK-core: android.hardware.radio.modem-V1-ndk.so VNDK-core: android.hardware.radio.network-V1-ndk.so VNDK-core: android.hardware.radio.sim-V1-ndk.so VNDK-core: android.hardware.radio.voice-V1-ndk.so VNDK-core: android.hardware.rebootescrow-V1-ndk.so VNDK-core: android.hardware.security.dice-V1-ndk.so VNDK-core: android.hardware.security.keymint-V2-ndk.so VNDK-core: android.hardware.security.secureclock-V1-ndk.so VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so VNDK-core: android.hardware.sensors-V1-ndk.so VNDK-core: android.hardware.soundtrigger3-V1-ndk.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hardware.usb-V1-ndk.so VNDK-core: android.hardware.uwb-V1-ndk.so VNDK-core: android.hardware.vibrator-V2-ndk.so VNDK-core: android.hardware.weaver-V1-ndk.so VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.media.audio.common.types-V1-ndk.so VNDK-core: android.media.soundtrigger.types-V1-ndk.so VNDK-core: android.system.keystore2-V2-ndk.so VNDK-core: android.system.suspend-V1-ndk.so VNDK-core: android.system.suspend@1.0.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libbacktrace.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-product: android.hardware.audio.common@2.0.so VNDK-product: android.hardware.configstore@1.0.so VNDK-product: android.hardware.configstore@1.1.so VNDK-product: android.hardware.graphics.allocator@2.0.so VNDK-product: android.hardware.graphics.allocator@3.0.so VNDK-product: android.hardware.graphics.allocator@4.0.so VNDK-product: android.hardware.graphics.bufferqueue@1.0.so VNDK-product: android.hardware.graphics.bufferqueue@2.0.so VNDK-product: android.hardware.graphics.common@1.0.so VNDK-product: android.hardware.graphics.common@1.1.so VNDK-product: android.hardware.graphics.common@1.2.so VNDK-product: android.hardware.graphics.mapper@2.0.so VNDK-product: android.hardware.graphics.mapper@2.1.so VNDK-product: android.hardware.graphics.mapper@3.0.so VNDK-product: android.hardware.graphics.mapper@4.0.so VNDK-product: android.hardware.media.bufferpool@2.0.so VNDK-product: android.hardware.media.omx@1.0.so VNDK-product: android.hardware.media@1.0.so VNDK-product: android.hardware.memtrack@1.0.so VNDK-product: android.hardware.renderscript@1.0.so VNDK-product: android.hardware.soundtrigger@2.0.so VNDK-product: android.hidl.memory.token@1.0.so VNDK-product: android.hidl.memory@1.0.so VNDK-product: android.hidl.safe_union@1.0.so VNDK-product: android.hidl.token@1.0.so VNDK-product: android.system.suspend@1.0.so VNDK-product: libaudioutils.so VNDK-product: libbacktrace.so VNDK-product: libbase.so VNDK-product: libc++.so VNDK-product: libcamera_metadata.so VNDK-product: libcap.so VNDK-product: libcompiler_rt.so VNDK-product: libcrypto.so VNDK-product: libcurl.so VNDK-product: libcutils.so VNDK-product: libevent.so VNDK-product: libexpat.so VNDK-product: libfmq.so VNDK-product: libhidlbase.so VNDK-product: libhidlmemory.so VNDK-product: libion.so VNDK-product: libjpeg.so VNDK-product: libjsoncpp.so VNDK-product: libldacBT_abr.so VNDK-product: libldacBT_enc.so VNDK-product: liblz4.so VNDK-product: liblzma.so VNDK-product: libminijail.so VNDK-product: libnl.so VNDK-product: libpcre2.so VNDK-product: libpiex.so VNDK-product: libpng.so VNDK-product: libprocessgroup.so VNDK-product: libprocinfo.so VNDK-product: libspeexresampler.so VNDK-product: libssl.so VNDK-product: libtinyalsa.so VNDK-product: libtinyxml2.so VNDK-product: libunwindstack.so VNDK-product: libutils.so VNDK-product: libutilscallstack.so VNDK-product: libwifi-system-iface.so VNDK-product: libxml2.so VNDK-product: libyuv.so VNDK-product: libz.so VNDK-product: libziparchive.so ================================================ FILE: target/product/gsi/34.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libcom.android.tethering.connectivity_native.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V2-ndk.so VNDK-SP: android.hardware.common.fmq-V1-ndk.so VNDK-SP: android.hardware.graphics.common-V4-ndk.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.composer3-V1-ndk.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0-impl.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libdmabufheap.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.frameworks.cameraservice.common-V1-ndk.so VNDK-core: android.frameworks.cameraservice.device-V1-ndk.so VNDK-core: android.frameworks.cameraservice.service-V1-ndk.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack-V1-ndk.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.system.suspend-V1-ndk.so VNDK-core: android.system.suspend@1.0.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdiskconfig.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-product: android.hardware.audio.common@2.0.so VNDK-product: android.hardware.configstore@1.0.so VNDK-product: android.hardware.configstore@1.1.so VNDK-product: android.hardware.graphics.allocator@2.0.so VNDK-product: android.hardware.graphics.allocator@3.0.so VNDK-product: android.hardware.graphics.allocator@4.0.so VNDK-product: android.hardware.graphics.bufferqueue@1.0.so VNDK-product: android.hardware.graphics.bufferqueue@2.0.so VNDK-product: android.hardware.graphics.common@1.0.so VNDK-product: android.hardware.graphics.common@1.1.so VNDK-product: android.hardware.graphics.common@1.2.so VNDK-product: android.hardware.graphics.mapper@2.0.so VNDK-product: android.hardware.graphics.mapper@2.1.so VNDK-product: android.hardware.graphics.mapper@3.0.so VNDK-product: android.hardware.graphics.mapper@4.0.so VNDK-product: android.hardware.media.bufferpool@2.0.so VNDK-product: android.hardware.media.omx@1.0.so VNDK-product: android.hardware.media@1.0.so VNDK-product: android.hardware.memtrack@1.0.so VNDK-product: android.hardware.renderscript@1.0.so VNDK-product: android.hardware.soundtrigger@2.0.so VNDK-product: android.hidl.memory.token@1.0.so VNDK-product: android.hidl.memory@1.0.so VNDK-product: android.hidl.safe_union@1.0.so VNDK-product: android.hidl.token@1.0.so VNDK-product: android.system.suspend@1.0.so VNDK-product: libaudioutils.so VNDK-product: libbase.so VNDK-product: libc++.so VNDK-product: libcamera_metadata.so VNDK-product: libcap.so VNDK-product: libcompiler_rt.so VNDK-product: libcrypto.so VNDK-product: libcurl.so VNDK-product: libcutils.so VNDK-product: libevent.so VNDK-product: libexpat.so VNDK-product: libfmq.so VNDK-product: libhidlbase.so VNDK-product: libhidlmemory.so VNDK-product: libion.so VNDK-product: libjpeg.so VNDK-product: libjsoncpp.so VNDK-product: libldacBT_abr.so VNDK-product: libldacBT_enc.so VNDK-product: liblz4.so VNDK-product: liblzma.so VNDK-product: libminijail.so VNDK-product: libnl.so VNDK-product: libpcre2.so VNDK-product: libpiex.so VNDK-product: libpng.so VNDK-product: libprocessgroup.so VNDK-product: libprocinfo.so VNDK-product: libspeexresampler.so VNDK-product: libssl.so VNDK-product: libtinyalsa.so VNDK-product: libtinyxml2.so VNDK-product: libunwindstack.so VNDK-product: libutils.so VNDK-product: libutilscallstack.so VNDK-product: libwifi-system-iface.so VNDK-product: libxml2.so VNDK-product: libyuv.so VNDK-product: libz.so VNDK-product: libziparchive.so ================================================ FILE: target/product/gsi/Android.bp ================================================ // Copyright 2020 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } filegroup { name: "vndk_lib_lists", srcs: [ "*.txt", ], } prebuilt_etc { name: "gsi_skip_mount.cfg", filename: "skip_mount.cfg", src: "gsi_skip_mount.cfg", system_ext_specific: true, relative_install_path: "init/config", required: ["gsi_skip_mount_compat_symlink"], } // Adds a symlink under /system/etc/init/config pointing to /system/system_ext/etc/init/config // because first-stage init in Android 10.0 will read the skip_mount.cfg from /system/etc/* after // chroot /system. // TODO: remove this symlink when no need to support new GSI on Android 10. // The actual file needs to be under /system/system_ext because it's GSI-specific and does not // belong to core CSI. install_symlink { name: "gsi_skip_mount_compat_symlink", installed_location: "etc/init/config", symlink_target: "/system/system_ext/etc/init/config", } // init.gsi.rc, GSI-specific init script. prebuilt_etc { name: "init.gsi.rc", src: "init.gsi.rc", system_ext_specific: true, relative_install_path: "init", } prebuilt_etc { name: "init.vndk-nodef.rc", src: "init.vndk-nodef.rc", system_ext_specific: true, relative_install_path: "gsi", } gsi_symlinks = [ { target: "/system/system_ext", name: "system_ext", }, { target: "/system/product", name: "product", }, { target: "/odm/odm_dlkm/etc", name: "odm_dlkm/etc", }, { target: "/vendor/vendor_dlkm/etc", name: "vendor_dlkm/etc", }, ] android_filesystem_defaults { name: "android_gsi_defaults", defaults: [ "system_image_defaults", "system_ext_image_defaults", "product_image_defaults", ], symlinks: gsi_symlinks, dirs: ["cache"], deps: [ /////////////////////////////////////////// // gsi_system_ext /////////////////////////////////////////// // handheld packages "Launcher3QuickStep", "Provision", "Settings", "StorageManager", "SystemUI", // telephony packages "CarrierConfig", /////////////////////////////////////////// // gsi_release /////////////////////////////////////////// "gsi_skip_mount.cfg", "init.gsi.rc", "init.vndk-nodef.rc", // Overlay the GSI specific setting for framework and SystemUI "gsi_overlay_framework", "gsi_overlay_systemui", /////////////////////////////////////////// // VNDK /////////////////////////////////////////// "com.android.vndk.v30", "com.android.vndk.v31", "com.android.vndk.v32", "com.android.vndk.v33", "com.android.vndk.v34", /////////////////////////////////////////// // gsi_product /////////////////////////////////////////// "Browser2", "Camera2", "Dialer", "LatinIME", "apns-full-conf.xml", "frameworks-base-overlays", ], multilib: { lib64: { deps: [ /////////////////////////////////////////// // AVF /////////////////////////////////////////// "com.android.compos", "features_com.android.virt.xml", ], }, both: { // PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 deps: ["android.hidl.memory@1.0-impl"], }, }, type: "ext4", } // system.img for gsi_{arch} targets android_system_image { name: "android_gsi", defaults: ["android_gsi_defaults"], enabled: select(soong_config_variable("ANDROID", "PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT"), { "true": true, default: false, }), deps: [ // Install a copy of the debug policy to the system_ext partition, and allow // init-second-stage to load debug policy from system_ext. // This option is only meant to be set by compliance GSI targets. "system_ext_userdebug_plat_sepolicy.cil", ], } // system.img for aosp_{arch} targets android_system_image { name: "aosp_system_image", defaults: ["android_gsi_defaults"], deps: [ // handheld_system_ext "AccessibilityMenu", "WallpaperCropper", // telephony_system_ext "EmergencyInfo", // handheld_product "Calendar", "Contacts", "DeskClock", "Gallery2", "Music", "preinstalled-packages-platform-handheld-product.xml", "QuickSearchBox", "SettingsIntelligence", "frameworks-base-overlays", // telephony_product "ImsServiceEntitlement", "preinstalled-packages-platform-telephony-product.xml", // more AOSP packages "initial-package-stopped-states-aosp.xml", "messaging", "PhotoTable", "preinstalled-packages-platform-aosp-product.xml", "ThemePicker", ] + select(product_variable("debuggable"), { true: ["frameworks-base-overlays-debug"], default: [], }), enabled: select(soong_config_variable("gsi", "building_gsi"), { true: true, default: false, }), multilib: { common: { deps: select(release_flag("RELEASE_AVATAR_PICKER_APP"), { true: [ "AvatarPicker", // handheld_system_ext (RELEASE_AVATAR_PICKER_APP) ], default: [], }), }, }, } ================================================ FILE: target/product/gsi/OWNERS ================================================ bowgotsai@google.com jiyong@google.com justinyun@google.com smoreland@google.com szuweilin@google.com yochiang@google.com ================================================ FILE: target/product/gsi/current.txt ================================================ LLNDK: libEGL.so LLNDK: libGLESv1_CM.so LLNDK: libGLESv2.so LLNDK: libGLESv3.so LLNDK: libRS.so LLNDK: libandroid_net.so LLNDK: libapexsupport.so LLNDK: libbinder_ndk.so LLNDK: libc.so LLNDK: libcgrouprc.so LLNDK: libcom.android.tethering.connectivity_native.so LLNDK: libdl.so LLNDK: libft2.so LLNDK: liblog.so LLNDK: libm.so LLNDK: libmediandk.so LLNDK: libnativewindow.so LLNDK: libneuralnetworks.so LLNDK: libselinux.so LLNDK: libsync.so LLNDK: libvendorsupport.so LLNDK: libvndksupport.so LLNDK: libvulkan.so VNDK-SP: android.hardware.common-V2-ndk.so VNDK-SP: android.hardware.common.fmq-V1-ndk.so VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so VNDK-SP: android.hardware.graphics.common-V6-ndk.so VNDK-SP: android.hardware.graphics.common@1.0.so VNDK-SP: android.hardware.graphics.common@1.1.so VNDK-SP: android.hardware.graphics.common@1.2.so VNDK-SP: android.hardware.graphics.composer3-V1-ndk.so VNDK-SP: android.hardware.graphics.mapper@2.0.so VNDK-SP: android.hardware.graphics.mapper@2.1.so VNDK-SP: android.hardware.graphics.mapper@3.0.so VNDK-SP: android.hardware.graphics.mapper@4.0.so VNDK-SP: android.hardware.renderscript@1.0.so VNDK-SP: android.hidl.memory.token@1.0.so VNDK-SP: android.hidl.memory@1.0.so VNDK-SP: android.hidl.safe_union@1.0.so VNDK-SP: libRSCpuRef.so VNDK-SP: libRSDriver.so VNDK-SP: libRS_internal.so VNDK-SP: libbase.so VNDK-SP: libbcinfo.so VNDK-SP: libblas.so VNDK-SP: libc++.so VNDK-SP: libcompiler_rt.so VNDK-SP: libcutils.so VNDK-SP: libdmabufheap.so VNDK-SP: libgralloctypes.so VNDK-SP: libhardware.so VNDK-SP: libhidlbase.so VNDK-SP: libhidlmemory.so VNDK-SP: libion.so VNDK-SP: libjsoncpp.so VNDK-SP: liblzma.so VNDK-SP: libprocessgroup.so VNDK-SP: libunwindstack.so VNDK-SP: libutils.so VNDK-SP: libutilscallstack.so VNDK-SP: libz.so VNDK-core: android.frameworks.cameraservice.common-V1-ndk.so VNDK-core: android.frameworks.cameraservice.device-V2-ndk.so VNDK-core: android.frameworks.cameraservice.service-V2-ndk.so VNDK-core: android.hardware.audio.common@2.0.so VNDK-core: android.hardware.configstore-utils.so VNDK-core: android.hardware.configstore@1.0.so VNDK-core: android.hardware.configstore@1.1.so VNDK-core: android.hardware.confirmationui-support-lib.so VNDK-core: android.hardware.graphics.allocator@2.0.so VNDK-core: android.hardware.graphics.allocator@3.0.so VNDK-core: android.hardware.graphics.allocator@4.0.so VNDK-core: android.hardware.graphics.bufferqueue@1.0.so VNDK-core: android.hardware.graphics.bufferqueue@2.0.so VNDK-core: android.hardware.media.bufferpool@2.0.so VNDK-core: android.hardware.media.omx@1.0.so VNDK-core: android.hardware.media@1.0.so VNDK-core: android.hardware.memtrack-V1-ndk.so VNDK-core: android.hardware.memtrack@1.0.so VNDK-core: android.hardware.soundtrigger@2.0-core.so VNDK-core: android.hardware.soundtrigger@2.0.so VNDK-core: android.hidl.token@1.0-utils.so VNDK-core: android.hidl.token@1.0.so VNDK-core: android.system.suspend-V1-ndk.so VNDK-core: android.system.suspend@1.0.so VNDK-core: libaudioroute.so VNDK-core: libaudioutils.so VNDK-core: libbinder.so VNDK-core: libbufferqueueconverter.so VNDK-core: libcamera_metadata.so VNDK-core: libcap.so VNDK-core: libcn-cbor.so VNDK-core: libcodec2.so VNDK-core: libcrypto.so VNDK-core: libcrypto_utils.so VNDK-core: libcurl.so VNDK-core: libdumpstateutil.so VNDK-core: libevent.so VNDK-core: libexif.so VNDK-core: libexpat.so VNDK-core: libfmq.so VNDK-core: libgatekeeper.so VNDK-core: libgui.so VNDK-core: libhardware_legacy.so VNDK-core: libhidlallocatorutils.so VNDK-core: libjpeg.so VNDK-core: libldacBT_abr.so VNDK-core: libldacBT_enc.so VNDK-core: liblz4.so VNDK-core: libmedia_helper.so VNDK-core: libmedia_omx.so VNDK-core: libmemtrack.so VNDK-core: libminijail.so VNDK-core: libmkbootimg_abi_check.so VNDK-core: libnetutils.so VNDK-core: libnl.so VNDK-core: libpcre2.so VNDK-core: libpiex.so VNDK-core: libpng.so VNDK-core: libpower.so VNDK-core: libprocinfo.so VNDK-core: libradio_metadata.so VNDK-core: libspeexresampler.so VNDK-core: libsqlite.so VNDK-core: libssl.so VNDK-core: libstagefright_bufferpool@2.0.so VNDK-core: libstagefright_bufferqueue_helper.so VNDK-core: libstagefright_foundation.so VNDK-core: libstagefright_omx.so VNDK-core: libstagefright_omx_utils.so VNDK-core: libstagefright_xmlparser.so VNDK-core: libsysutils.so VNDK-core: libtinyalsa.so VNDK-core: libtinyxml2.so VNDK-core: libui.so VNDK-core: libusbhost.so VNDK-core: libwifi-system-iface.so VNDK-core: libxml2.so VNDK-core: libyuv.so VNDK-core: libziparchive.so VNDK-core: server_configurable_flags.so VNDK-private: libblas.so VNDK-private: libcompiler_rt.so VNDK-private: libft2.so VNDK-private: libgui.so VNDK-product: android.hardware.audio.common@2.0.so VNDK-product: android.hardware.configstore@1.0.so VNDK-product: android.hardware.configstore@1.1.so VNDK-product: android.hardware.graphics.allocator@2.0.so VNDK-product: android.hardware.graphics.allocator@3.0.so VNDK-product: android.hardware.graphics.allocator@4.0.so VNDK-product: android.hardware.graphics.bufferqueue@1.0.so VNDK-product: android.hardware.graphics.bufferqueue@2.0.so VNDK-product: android.hardware.graphics.common@1.0.so VNDK-product: android.hardware.graphics.common@1.1.so VNDK-product: android.hardware.graphics.common@1.2.so VNDK-product: android.hardware.graphics.mapper@2.0.so VNDK-product: android.hardware.graphics.mapper@2.1.so VNDK-product: android.hardware.graphics.mapper@3.0.so VNDK-product: android.hardware.graphics.mapper@4.0.so VNDK-product: android.hardware.media.bufferpool@2.0.so VNDK-product: android.hardware.media.omx@1.0.so VNDK-product: android.hardware.media@1.0.so VNDK-product: android.hardware.memtrack@1.0.so VNDK-product: android.hardware.renderscript@1.0.so VNDK-product: android.hardware.soundtrigger@2.0.so VNDK-product: android.hidl.memory.token@1.0.so VNDK-product: android.hidl.memory@1.0.so VNDK-product: android.hidl.safe_union@1.0.so VNDK-product: android.hidl.token@1.0.so VNDK-product: android.system.suspend@1.0.so VNDK-product: libaudioutils.so VNDK-product: libbase.so VNDK-product: libc++.so VNDK-product: libcamera_metadata.so VNDK-product: libcap.so VNDK-product: libcompiler_rt.so VNDK-product: libcrypto.so VNDK-product: libcurl.so VNDK-product: libcutils.so VNDK-product: libevent.so VNDK-product: libexpat.so VNDK-product: libfmq.so VNDK-product: libhidlbase.so VNDK-product: libhidlmemory.so VNDK-product: libion.so VNDK-product: libjpeg.so VNDK-product: libjsoncpp.so VNDK-product: libldacBT_abr.so VNDK-product: libldacBT_enc.so VNDK-product: liblz4.so VNDK-product: liblzma.so VNDK-product: libminijail.so VNDK-product: libnl.so VNDK-product: libpcre2.so VNDK-product: libpiex.so VNDK-product: libpng.so VNDK-product: libprocessgroup.so VNDK-product: libprocinfo.so VNDK-product: libspeexresampler.so VNDK-product: libssl.so VNDK-product: libtinyalsa.so VNDK-product: libtinyxml2.so VNDK-product: libunwindstack.so VNDK-product: libutils.so VNDK-product: libutilscallstack.so VNDK-product: libwifi-system-iface.so VNDK-product: libxml2.so VNDK-product: libyuv.so VNDK-product: libz.so VNDK-product: libziparchive.so VNDK-product: server_configurable_flags.so ================================================ FILE: target/product/gsi/gsi_skip_mount.cfg ================================================ # Skip "system" mountpoints. /oem /product /system_ext # Skip sub-mountpoints of system mountpoints. /oem/* /product/* /system_ext/* /system/* ================================================ FILE: target/product/gsi/init.gsi.rc ================================================ # # Android init script for GSI required initialization # import /system/system_ext/etc/gsi/init.vndk-${ro.vndk.version:-nodef}.rc ================================================ FILE: target/product/gsi/init.vndk-nodef.rc ================================================ on early-init # Reboot if BOARD_VNDK_VERSION is not defined exec - root -- /system/bin/reboot bootloader ================================================ FILE: target/product/gsi/testkey_rsa2048.pem ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA3fDgwU4JKVRHhAfofi/g8daTNplB2mTJCX9fIMy9FnZDXNij 1zijRQ8HKbt3bAGImQvb3GxSV4M5eIdiLDUF7RsUpE7K+s939i/AaTtcuyqimQbJ QjP9emTsgngHzuKWMg1mwlRZYDfdv62zIQmZcbM9a0CZE36hAYvEBiDB8qT4ob++ godGAx3rpF2Wi7mhIYDINvkCw8/16Qi9CZgvOUrEolt3mz8Sps41z9j7YAsPbAa8 fg7dUu61s6NkZEykl4G67loOaf7h+SyP//LpFZ0gV+STZ+EMGofL0SXb8A+hdIYE QxsnKUYo8e+GaQg92FLxVZqcfyG3AZuMB04R1QIDAQABAoIBAQDGj3/1UaSepjlJ ZW3an2lH1Cpm2ZxyEGNQLPVluead1vaTdXq3zYM9AKHu8zp3lbOpAVQVk4/jnZJo Q+9QD6waonTIP3oYBE+WIMirHSHsjctkzw52PV9VBkAWxd5ueIfZheXejGpdy/2H RJcTQqxWbf7QGr4ZE9xmLq4UsW/zbXwy8qGEp9eMQIIaWBua43FkqmWYLSnVFVJI Gl8mfVJctLNSZHhS3tKiV8up6NxZlDjO8o7kYVFCkv0xJ9yzQNBc3P2MEmvfZ06D QnimHBqSxr0M9X6hqP43CnqtCbpsHS8A12Dm4l6fkXfkrAY0UNrEaCSDb8aN7TEc 7bc1MB4NAoGBAPK7xSuvQE9CH05Iy+G6mEQTtNmpfcQosqhi6dF60h4bqlkeGzUu gF/PKHwwffHAxQSv4V831P3A/IoJFa9IFkg218mYPNfzpj4vJA4aNCDp+SYZAIYm h6hMOmuByI97wds2yCBGt4mP0eow5B3A1b3UQeqW6LVSuobZ22QVlSk/AoGBAOoS L82yda9hUa7vuXtqTraf9EGjSXhyjoPqWxa+a1ooI9l24f7mokS5Iof+a/SLfPUj pwj8eOeOZksjAaWJIdrRb3TaYLaqhDkWQeV5N5XxYbn3+TvVJQyR+OSBfGoEpVP/ IS6fusvpT3eULJDax10By+gDcoLT5M1FNs4rBIvrAoGBAM8yJP5DHDwLjzl9vjsy 0iLaR3e8zBQTQV2nATvFAXKd3u0vW74rsX0XEdHgesFP8V0s3M4wlGj+wRL66j2y 5QJDfjMg9l7IJlHSX46CI5ks33X7xYy9evLYDs4R/Kct1q5OtsmGU8jisSadETus jUb61kFvC7krovjVIgbuvWJ1AoGAVikzp4gVgeVU6AwePqu3JcpjYvX0SX4Br9VI imq1oY49BAOa1PWYratoZp7kpjPiX2osRkaJStNEHExagtCjwaRuXpk0GIlT+p+S yiGAsJUV4BrDh57B8IqbD6IKZgwnv2+ei0cIv562PdIxRXEDCd1rbZA3SqktA9KC hgmXttkCgYBPU1lqRpwoHP9lpOBTDa6/Xi6WaDEWrG/tUF/wMlvrZ4hEVMDJRs1d 9JCXBxL/O4TMvpmyVKBZW15iZOcLM3EpiZ00UD+ChcAaFstup+oYKrs8gL9hgyTd cvWMxGQm13KwSj2CLzEQpPAN5xG14njXaee5ksshxkzBz9z3MVWiiw== -----END RSA PRIVATE KEY----- ================================================ FILE: target/product/gsi_release.mk ================================================ # # Copyright (C) 2019 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # The makefile contains the special settings for GSI releasing. # This makefile is used for the build targets which used for releasing GSI. # # For example: # - Released GSI contains skip_mount.cfg to skip mounting prodcut paritition # - Released GSI contains more VNDK packages to support old version vendors # - etc. # # See device/generic/common/README.md for more details. # BUILDING_GSI := true PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \ system/etc/init/config \ system/product/% \ system/system_ext/% # GSI should always support up-to-date platform features. # Keep this value at the latest API level to ensure latest build system # default configs are applied. PRODUCT_SHIPPING_API_LEVEL := 34 # Enable dynamic partitions to facilitate mixing onto Cuttlefish PRODUCT_USE_DYNAMIC_PARTITIONS := true # Enable dynamic partition size PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true # GSI specific tasks on boot PRODUCT_PACKAGES += \ gsi_skip_mount.cfg \ init.gsi.rc \ init.vndk-nodef.rc \ # Overlay the GSI specific setting for framework and SystemUI ifneq ($(PRODUCT_IS_AUTOMOTIVE),true) PRODUCT_PACKAGES += \ gsi_overlay_framework \ gsi_overlay_systemui \ PRODUCT_COPY_FILES += \ device/generic/common/overlays/overlay-config.xml:$(TARGET_COPY_OUT_SYSTEM_EXT)/overlay/config/config.xml endif # Support additional VNDK snapshots PRODUCT_EXTRA_VNDK_VERSIONS := \ 30 \ 31 \ 32 \ 33 \ 34 \ # Do not build non-GSI partition images. PRODUCT_BUILD_CACHE_IMAGE := false PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false PRODUCT_BUILD_USERDATA_IMAGE := false PRODUCT_BUILD_VENDOR_IMAGE := false PRODUCT_BUILD_SUPER_PARTITION := false PRODUCT_BUILD_SUPER_EMPTY_IMAGE := false PRODUCT_BUILD_SYSTEM_DLKM_IMAGE := false PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true # Build pvmfw with GSI: b/376363989, pvmfw currently only supports AArch64 ifneq (,$(filter %_arm64,$(TARGET_PRODUCT))) PRODUCT_BUILD_PVMFW_IMAGE := true endif # Additional settings used in all GSI builds PRODUCT_PRODUCT_PROPERTIES += \ ro.crypto.metadata_init_delete_all_keys.enabled=false \ debug.codec2.bqpool_dealloc_after_stop=1 \ # Window Extensions ifneq ($(PRODUCT_IS_ATV),true) $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk) endif # A GSI is to be mixed with different boot images. That means we can't determine # the kernel version when building a GSI. # Assume the device supports UFFD. If it doesn't, the ART runtime will fall back # to CC, and odrefresh will regenerate core dexopt artifacts on the first boot, # so this is okay. PRODUCT_ENABLE_UFFD_GC := true ================================================ FILE: target/product/handheld_product.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the product partition contents for # a generic phone or tablet device. Only add something here if # it definitely doesn't belong on other types of devices (if it # does, use base_product.mk). $(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk) # /product packages PRODUCT_PACKAGES += \ Browser2 \ Calendar \ Camera2 \ Contacts \ DeskClock \ Gallery2 \ LatinIME \ Music \ preinstalled-packages-platform-handheld-product.xml \ QuickSearchBox \ SettingsIntelligence \ frameworks-base-overlays PRODUCT_PACKAGES_DEBUG += \ frameworks-base-overlays-debug ================================================ FILE: target/product/handheld_system.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the system partition contents for # a generic phone or tablet device. Only add something here if # it definitely doesn't belong on other types of devices (if it # does, use base_vendor.mk). $(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk) $(call inherit-product-if-exists, frameworks/base/data/fonts/fonts.mk) $(call inherit-product-if-exists, external/google-fonts/dancing-script/fonts.mk) $(call inherit-product-if-exists, external/google-fonts/carrois-gothic-sc/fonts.mk) $(call inherit-product-if-exists, external/google-fonts/coming-soon/fonts.mk) $(call inherit-product-if-exists, external/google-fonts/cutive-mono/fonts.mk) $(call inherit-product-if-exists, external/google-fonts/source-sans-pro/fonts.mk) $(call inherit-product-if-exists, external/noto-fonts/fonts.mk) $(call inherit-product-if-exists, external/roboto-fonts/fonts.mk) $(call inherit-product-if-exists, external/roboto-flex-fonts/fonts.mk) $(call inherit-product-if-exists, external/hyphenation-patterns/patterns.mk) $(call inherit-product-if-exists, frameworks/base/data/keyboards/keyboards.mk) $(call inherit-product-if-exists, frameworks/webview/chromium/chromium.mk) PRODUCT_PACKAGES += \ android.software.window_magnification.prebuilt.xml \ BasicDreams \ BlockedNumberProvider \ BluetoothMidiService \ BookmarkProvider \ BuiltInPrintService \ CalendarProvider \ cameraserver \ CameraExtensionsProxy \ CaptivePortalLogin \ CertInstaller \ CredentialManager \ DeviceAsWebcam \ DeviceDiagnostics \ DocumentsUI \ DownloadProviderUi \ EasterEgg \ ExternalStorageProvider \ FusedLocation \ InputDevices \ KeyChain \ librs_jni \ ManagedProvisioning \ MmsService \ MtpService \ MusicFX \ PacProcessor \ preinstalled-packages-platform-handheld-system.xml \ PrintRecommendationService \ PrintSpooler \ ProxyHandler \ screenrecord \ SecureElement \ SharedStorageBackup \ SimAppDialog \ Telecom \ TeleService \ Traceur \ UserDictionaryProvider \ VpnDialogs \ vr \ # Choose the correct products based on HSUM status ifeq ($(PRODUCT_USE_HSUM),true) PRODUCT_PACKAGES += TelephonyProviderHsum else PRODUCT_PACKAGES += TelephonyProvider endif PRODUCT_PACKAGES += $(RELEASE_PACKAGE_VIRTUAL_CAMERA) # Set virtual_camera_service_enabled soong config variable based on the # RELEASE_PACKAGE_VIRTUAL_CAMERA build. virtual_camera_service_enabled soong config # variable is used to prevent accessing the service when it's not present in the build. $(call soong_config_set,vdm,virtual_camera_service_enabled,$(if $(RELEASE_PACKAGE_VIRTUAL_CAMERA),true,false)) PRODUCT_SYSTEM_SERVER_APPS += \ FusedLocation \ InputDevices \ KeyChain \ Telecom \ PRODUCT_PACKAGES += framework-audio_effects.xml PRODUCT_VENDOR_PROPERTIES += \ ro.carrier?=unknown \ ro.config.notification_sound?=OnTheHunt.ogg \ ro.config.alarm_alert?=Alarm_Classic.ogg ================================================ FILE: target/product/handheld_system_ext.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the system_ext partition contents for # a generic phone or tablet device. Only add something here if # it definitely doesn't belong on other types of devices (if it # does, use base_system_ext.mk). $(call inherit-product, $(SRC_TARGET_DIR)/product/media_system_ext.mk) # /system_ext packages PRODUCT_PACKAGES += \ AccessibilityMenu \ $(if $(RELEASE_AVATAR_PICKER_APP), AvatarPicker,) \ Launcher3QuickStep \ Provision \ Settings \ StorageManager \ SystemUI \ WallpaperCropper \ ================================================ FILE: target/product/handheld_vendor.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the non-system partition contents for # a generic phone or tablet device. Only add something here if # it definitely doesn't belong on other types of devices (if it # does, use base_vendor.mk). $(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk) # /vendor packages PRODUCT_PACKAGES += \ audio.primary.default \ local_time.default \ power.default \ vibrator.default \ ================================================ FILE: target/product/hsum_common.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Contains common default elements for devices running in Headless System User Mode. # Should generally be inherited first as using an HSUM configuration can affect downstream choices # (such as ensuring that the HSUM-variants of packages are selected). PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \ ro.fw.mu.headless_system_user=true # Variable for elsewhere choosing the appropriate products based on HSUM status. PRODUCT_USE_HSUM := true PRODUCT_PACKAGES += \ HsumDefaultConfigOverlay ================================================ FILE: target/product/languages_default.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration that just contains a list of languages, with # en_US set as the default language. PRODUCT_LOCALES := \ en_US \ af_ZA \ am_ET \ ar_EG \ ar_XB \ as_IN \ az_AZ \ be_BY \ bg_BG \ bn_BD \ bs_BA \ ca_ES \ cs_CZ \ da_DK \ de_DE \ el_GR \ en_AU \ en_CA \ en_GB \ en_IN \ en_XA \ es_ES \ es_US \ et_EE \ eu_ES \ fa_IR \ fi_FI \ fr_CA \ fr_FR \ gl_ES \ gu_IN \ hi_IN \ hr_HR \ hu_HU \ hy_AM \ in_ID \ is_IS \ it_IT \ iw_IL \ ja_JP \ ka_GE \ kk_KZ \ km_KH \ kn_IN \ ko_KR \ ky_KG \ lo_LA \ lt_LT \ lv_LV \ mk_MK \ ml_IN \ mn_MN \ mr_IN \ ms_MY \ my_MM \ nb_NO \ ne_NP \ nl_NL \ or_IN \ pa_IN \ pl_PL \ pt_BR \ pt_PT \ ro_RO \ ru_RU \ si_LK \ sk_SK \ sl_SI \ sq_AL \ sr_Latn_RS \ sr_RS \ sv_SE \ sw_TZ \ ta_IN \ te_IN \ th_TH \ tl_PH \ tr_TR \ uk_UA \ ur_PK \ uz_UZ \ vi_VN \ zh_CN \ zh_HK \ zh_TW \ zu_ZA \ ================================================ FILE: target/product/languages_full.mk ================================================ # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a build configuration that contains the default list of languages, # as well as the en_XC pseudo-locale, which is useful for localization test # builds. $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) PRODUCT_LOCALES += en_XC ================================================ FILE: target/product/large_screen_common.mk ================================================ # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Window Extensions $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk) # Enable Settings 2-pane optimization for large-screen PRODUCT_SYSTEM_PROPERTIES += \ persist.settings.large_screen_opt.enabled=true ================================================ FILE: target/product/linux_bionic.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_NAME := linux_bionic PRODUCT_BRAND := Android PRODUCT_DEVICE := linux_bionic ================================================ FILE: target/product/mainline_sdk.mk ================================================ # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_NAME := mainline_sdk PRODUCT_BRAND := Android PRODUCT_DEVICE := mainline_sdk PRODUCT_BUILD_FROM_SOURCE_STUB := true ================================================ FILE: target/product/mainline_system_arm64.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Do not modify this file. It's just alias of generic_system_arm64.mk # Will be removed when renaming from mainline_system to generic_system # complete $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_arm64.mk) PRODUCT_NAME := mainline_system_arm64 ================================================ FILE: target/product/mainline_system_x86.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Do not modify this file. It's just alias of generic_system_x86.mk # Will be removed when renaming from mainline_system to generic_system # complete $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86.mk) PRODUCT_NAME := mainline_system_x86 ================================================ FILE: target/product/mainline_system_x86_64.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Do not modify this file. It's just alias of generic_system_x86_64.mk # Will be removed when renaming from mainline_system to generic_system # complete $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_64.mk) PRODUCT_NAME := mainline_system_x86_64 ================================================ FILE: target/product/mainline_system_x86_arm.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Do not modify this file. It's just alias of generic_system_x86_arm.mk # Will be removed when renaming from mainline_system to generic_system # complete $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system_x86_arm.mk) PRODUCT_NAME := mainline_system_x86_arm ================================================ FILE: target/product/media_product.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the product partition contents for # media-capable devices (non-wearables). Only add something here # if it definitely doesn't belong on wearables. Otherwise, choose # base_product.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk) # /product packages PRODUCT_PACKAGES += \ webview \ ================================================ FILE: target/product/media_system.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the system partition contents for # media-capable devices (non-wearables). Only add something # here if it definitely doesn't belong on wearables. Otherwise, # choose base_system.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk) PRODUCT_PACKAGES += \ android.software.webview.prebuilt.xml \ com.android.future.usb.accessory \ com.android.mediadrm.signer \ com.android.media.remotedisplay \ com.android.media.remotedisplay.xml \ CompanionDeviceManager \ drmserver \ fsck.f2fs \ HTMLViewer \ libfilterpack_imageproc \ libwebviewchromium_loader \ libwebviewchromium_plat_support \ make_f2fs \ requestsync \ PRODUCT_HOST_PACKAGES += \ fsck.f2fs \ ifneq (REL,$(PLATFORM_VERSION_CODENAME)) PRODUCT_PACKAGES += \ android.software.preview_sdk.prebuilt.xml endif # The order here is the same order they end up on the classpath, so it matters. PRODUCT_SYSTEM_SERVER_JARS := \ com.android.location.provider \ services PRODUCT_COPY_FILES += \ system/core/rootdir/etc/public.libraries.android.txt:system/etc/public.libraries.txt # Enable boot.oat filtering of compiled classes to reduce boot.oat size. b/28026683 PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\ frameworks/base/config/compiled-classes-phone:system/etc/compiled-classes) # On userdebug builds, collect more tombstones by default. ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) PRODUCT_VENDOR_PROPERTIES += \ tombstoned.max_tombstone_count?=50 endif PRODUCT_VENDOR_PROPERTIES += \ ro.logd.size.stats=64K \ log.tag.stats_log=I # Enable CFI for security-sensitive components $(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk) $(call inherit-product-if-exists, vendor/google/products/cfi-vendor.mk) # Enable MTE for security-sensitive components $(call inherit-product, $(SRC_TARGET_DIR)/product/memtag-common.mk) $(call inherit-product-if-exists, vendor/google/products/memtag-vendor.mk) ================================================ FILE: target/product/media_system_ext.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the system_ext partition contents for # media-capable devices (non-wearables). Only add something here # if it definitely doesn't belong on wearables. Otherwise, choose # base_system_ext.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk) PRODUCT_PACKAGES += \ StatementService \ # Window Extensions $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions_base.mk) ================================================ FILE: target/product/media_vendor.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This makefile contains the non-system partition contents for # media-capable devices (non-wearables). Only add something here # if it definitely doesn't belong on wearables. Otherwise, choose # base_vendor.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk) # /vendor packages PRODUCT_PACKAGES += \ libaudiopreprocessing \ ================================================ FILE: target/product/memtag-common.mk ================================================ # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a recommended set of common components to enable MTE for. PRODUCT_MEMTAG_HEAP_ASYNC_DEFAULT_INCLUDE_PATHS := \ external/android-clat \ external/iproute2 \ external/iptables \ external/mtpd \ external/ppp \ hardware/st/nfc \ hardware/st/secure_element \ hardware/st/secure_element2 \ packages/modules/StatsD \ system/bpf \ system/netd/netutil_wrappers \ system/netd/server ================================================ FILE: target/product/module_arm.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) PRODUCT_NAME := module_arm PRODUCT_DEVICE := module_arm ================================================ FILE: target/product/module_arm64.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) PRODUCT_NAME := module_arm64 PRODUCT_DEVICE := module_arm64 PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true PRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384 ================================================ FILE: target/product/module_arm64only.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) PRODUCT_NAME := module_arm64only PRODUCT_DEVICE := module_arm64only PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true PRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384 ================================================ FILE: target/product/module_common.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/memtag-common.mk) # Enables treble, which enabled certain -D compilation flags. In particular, libhidlbase # uses -DENFORCE_VINTF_MANIFEST. See b/185759877 PRODUCT_SHIPPING_API_LEVEL := 29 # If true, this builds the mainline modules from source. This overrides any # prebuilts selected via RELEASE_APEX_CONTRIBUTIONS_* build flags for the # current release config. PRODUCT_MODULE_BUILD_FROM_SOURCE := true # Build sdk from source if the branch is not using slim manifests. ifneq (,$(strip $(wildcard frameworks/base/Android.bp))) UNBUNDLED_BUILD_SDKS_FROM_SOURCE := true endif PRODUCT_BRAND := Android ================================================ FILE: target/product/module_riscv64.mk ================================================ # # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) PRODUCT_NAME := module_riscv64 PRODUCT_DEVICE := module_riscv64 ================================================ FILE: target/product/module_x86.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) PRODUCT_NAME := module_x86 PRODUCT_DEVICE := module_x86 ================================================ FILE: target/product/module_x86_64.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) PRODUCT_NAME := module_x86_64 PRODUCT_DEVICE := module_x86_64 PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true PRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384 ================================================ FILE: target/product/module_x86_64only.mk ================================================ # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/module_common.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) PRODUCT_NAME := module_x86_64only PRODUCT_DEVICE := module_x86_64only PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true PRODUCT_MAX_PAGE_SIZE_SUPPORTED := 16384 ================================================ FILE: target/product/ndk.mk ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This device is suitable for soong-only build that builds for all the architectures # needed for the ndk. It is not going to work for normal `lunch && m` workflows. PRODUCT_NAME := ndk PRODUCT_BRAND := Android PRODUCT_DEVICE := ndk PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true ================================================ FILE: target/product/non_ab_device.mk ================================================ # Packages to update the recovery partition, which will be installed on # /vendor. Don't install these unless they're needed. PRODUCT_PACKAGES += \ applypatch ================================================ FILE: target/product/product_launched_with_k.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 19 ================================================ FILE: target/product/product_launched_with_l.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 21 ================================================ FILE: target/product/product_launched_with_l_mr1.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 22 ================================================ FILE: target/product/product_launched_with_m.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 23 ================================================ FILE: target/product/product_launched_with_n.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 24 ================================================ FILE: target/product/product_launched_with_n_mr1.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 25 ================================================ FILE: target/product/product_launched_with_o.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 26 ================================================ FILE: target/product/product_launched_with_o_mr1.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 27 ================================================ FILE: target/product/product_launched_with_p.mk ================================================ #PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launched on. PRODUCT_SHIPPING_API_LEVEL := 28 ================================================ FILE: target/product/profile_boot_common.mk ================================================ # # Copyright 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Use an empty profile to make non of the boot image be AOT compiled (for now). # Note that we could use a previous profile but we will miss the opportunity to # remove classes that are no longer in use. # Ideally we would just generate an empty boot.art but we don't have the build # support to separate the image from the compile code. PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := build/make/target/product/empty-profile DEX_PREOPT_DEFAULT := nostripping # Boot image property overrides. PRODUCT_VENDOR_PROPERTIES += \ dalvik.vm.profilesystemserver=true \ dalvik.vm.profilebootclasspath=true PRODUCT_DIST_BOOT_AND_SYSTEM_JARS := true ================================================ FILE: target/product/ramdisk_stub.mk ================================================ # # Copyright 2022 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_COPY_FILES += \ build/make/target/product/ramdisk_stub.mk:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/nonempty ================================================ FILE: target/product/runtime_libart.mk ================================================ # # Copyright (C) 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Provides a functioning ART environment without Android frameworks $(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk) # Additional mixins to the boot classpath. PRODUCT_PACKAGES += \ android.test.base \ # Why are we pulling in ext, which is frameworks/base, depending on tagsoup and nist-sip? PRODUCT_PACKAGES += \ ext \ # Runtime (Bionic) APEX module. PRODUCT_PACKAGES += com.android.runtime # ART APEX module. # # Select either release (com.android.art) or debug (com.android.art.debug) # variant of the ART APEX. By default, "user" build variants contain the release # module, while the "eng" build variant contain the debug module. However, if # `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is defined, it overrides the previous # logic: # - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `false`, the # build will include the release module (whatever the build # variant); # - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `true`, the # build will include the debug module (whatever the build variant). # # Note that the ART APEX package includes the minimal boot classpath JARs # (listed in ART_APEX_JARS), which should no longer be added directly to # PRODUCT_PACKAGES. art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD) ifneq (false,$(art_target_include_debug_build)) ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT))) art_target_include_debug_build := true endif endif ifeq (true,$(art_target_include_debug_build)) PRODUCT_PACKAGES += com.android.art.debug apex_test_module := art-check-debug-apex-gen-fakebin else PRODUCT_PACKAGES += com.android.art apex_test_module := art-check-release-apex-gen-fakebin endif ifeq (true,$(call soong_config_get,art_module,source_build)) PRODUCT_HOST_PACKAGES += $(apex_test_module) endif art_target_include_debug_build := apex_test_module := # Certificates. PRODUCT_PACKAGES += \ cacerts \ PRODUCT_PACKAGES += \ hiddenapi-package-whitelist.xml \ ifeq (,$(TARGET_BUILD_UNBUNDLED)) # Don't depend on the framework boot image profile in unbundled builds where # frameworks/base may not be present. # TODO(b/179900989): We may not need this check once we stop using full # platform products on the thin ART manifest branch. PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION += frameworks/base/boot/boot-image-profile.txt endif # The dalvik.vm.dexopt.thermal-cutoff property must contain one of the values # listed here: # # https://source.android.com/devices/architecture/hidl/thermal-mitigation#thermal-api # # If the thermal status of the device reaches or exceeds the value set here # background dexopt will be terminated and rescheduled using an exponential # backoff polcy. # # The thermal cutoff value is currently set to THERMAL_STATUS_MODERATE. PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.usejit=true \ dalvik.vm.dexopt.secondary=true \ dalvik.vm.dexopt.thermal-cutoff=2 \ dalvik.vm.appimageformat=lz4 PRODUCT_SYSTEM_PROPERTIES += \ ro.dalvik.vm.native.bridge?=0 # The install filter is speed-profile in order to enable the use of # profiles from the dex metadata files. Note that if a profile is not provided # or if it is empty speed-profile is equivalent to (quicken + empty app image). # Note that `cmdline` is not strictly needed but it simplifies the management # of compilation reason in the platform (as we have a unified, single path, # without exceptions). # TODO(b/243646876): Remove `pm.dexopt.post-boot`. PRODUCT_SYSTEM_PROPERTIES += \ pm.dexopt.post-boot?=verify \ pm.dexopt.first-boot?=verify \ pm.dexopt.boot-after-ota?=verify \ pm.dexopt.boot-after-mainline-update?=verify \ pm.dexopt.install?=speed-profile \ pm.dexopt.install-fast?=skip \ pm.dexopt.install-bulk?=speed-profile \ pm.dexopt.install-bulk-secondary?=verify \ pm.dexopt.install-bulk-downgraded?=verify \ pm.dexopt.install-bulk-secondary-downgraded?=verify \ pm.dexopt.bg-dexopt?=speed-profile \ pm.dexopt.ab-ota?=speed-profile \ pm.dexopt.inactive?=verify \ pm.dexopt.cmdline?=verify \ pm.dexopt.shared?=speed ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT))) OVERRIDE_DISABLE_DEXOPT_ALL ?= true endif # OVERRIDE_DISABLE_DEXOPT_ALL disables all dexpreopt (build-time) and dexopt (on-device) activities. # This option is for faster iteration during development and should never be enabled for production. ifneq (,$(filter true,$(OVERRIDE_DISABLE_DEXOPT_ALL))) PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.disable-art-service-dexopt=true \ dalvik.vm.disable-odrefresh=true # Disable all dexpreopt activities except for the ART boot image. # We have to dexpreopt the ART boot image because they are used by ART tests. This should not # be too much of a problem for platform developers because a change to framework code should not # trigger dexpreopt for the ART boot image. WITH_DEXPREOPT_ART_BOOT_IMG_ONLY := true $(call soong_config_set_bool,PrebuiltGmsCore,ExcludeExtractApk,true) endif # Enable resolution of startup const strings. PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.dex2oat-resolve-startup-strings=true # Specify default block size of 512K to enable parallel image decompression. PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.dex2oat-max-image-block-size=524288 # Enable minidebuginfo generation unless overridden. PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.minidebuginfo=true \ dalvik.vm.dex2oat-minidebuginfo=true # Enable Madvising of the whole odex and vdex files to MADV_WILLNEED. # The size specified here is the size limit of how much of the file # (in bytes) is madvised. # For odex and vdex files, we limit madvising to 100MB. # For art files, we defer to the runtime for default behavior. PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.madvise.vdexfile.size=104857600 \ dalvik.vm.madvise.odexfile.size=104857600 # Properties for the Unspecialized App Process Pool PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.usap_pool_enabled?=false \ dalvik.vm.usap_refill_threshold?=1 \ dalvik.vm.usap_pool_size_max?=3 \ dalvik.vm.usap_pool_size_min?=1 \ dalvik.vm.usap_pool_refill_delay_ms?=3000 PRODUCT_SYSTEM_PROPERTIES += \ dalvik.vm.useartservice=true \ dalvik.vm.enable_pr_dexopt=true # Copy preopted files from system_b on first boot. PRODUCT_SYSTEM_PROPERTIES += ro.cp_system_other_odex=1 PRODUCT_PACKAGES += \ cppreopts.sh ================================================ FILE: target/product/sdk.mk ================================================ # # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a simple product that uses configures the minimum amount # needed to build the SDK (without the emulator). # Ensure all trunk-stable flags are available. $(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk) # In order to build the bootclasspath sources, the bootclasspath needs to # be setup via default_art_config.mk. The sources only really make sense # together with a device (e.g. the emulator). So if the SDK sources change # to be built with the device, this could be removed. $(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk) PRODUCT_NAME := sdk PRODUCT_BRAND := Android PRODUCT_DEVICE := mainline_x86 PRODUCT_BUILD_FROM_SOURCE_STUB := true # Use sources of mainline modules PRODUCT_MODULE_BUILD_FROM_SOURCE := true ifeq ($(WITHOUT_CHECK_API),true) $(error WITHOUT_CHECK_API cannot be set to true for SDK product builds) endif # Include Wear flag values so that Wear-related APIs are build in sdks. PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/wear/release/release_config_map.textproto) ================================================ FILE: target/product/sdk_with_runtime_apis.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk) PRODUCT_NAME := sdk_with_runtime_apis PRODUCT_HIDDEN_API_EXPORTABLE_STUBS := true PRODUCT_EXPORT_RUNTIME_APIS := true ================================================ FILE: target/product/security/Android.bp ================================================ // AOSP test certificate package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } android_app_certificate { name: "aosp-testkey", certificate: "testkey", } // Certificate for CTS tests that rely on UICC hardware conforming to the // updated CTS UICC card specification introduced in 2021. See // //cts/tests/tests/carrierapi/Android.bp for more details. android_app_certificate { name: "cts-uicc-2021-testkey", certificate: "cts_uicc_2021", } // Google-owned certificate for CTS testing, since we can't trust arbitrary keys // on release devices. prebuilt_etc { name: "fsverity-release-cert-der", src: "fsverity-release.x509.der", sub_dir: "security/fsverity", filename_from_src: true, } // otacerts: A keystore with the authorized keys in it, which is used to verify // the authenticity of downloaded OTA packages. // This module zips files defined in PRODUCT_DEFAULT_DEV_CERTIFICATE and // PRODUCT_EXTRA_OTA_KEYS for system or PRODUCT_EXTRA_RECOVERY_KEYS for recovery // image otacerts_zip { name: "otacerts", relative_install_path: "security", filename: "otacerts.zip", } otacerts_zip { name: "otacerts.recovery", recovery: true, relative_install_path: "security", filename: "otacerts.zip", } adb_keys { name: "adb_keys", product_specific: true, } ================================================ FILE: target/product/security/README ================================================ For detailed information on key types and image signing, please see: https://source.android.com/devices/tech/ota/sign_builds.html The test keys in this directory are used in development only and should NEVER be used to sign packages in publicly released images (as that would open a major security hole). key generation -------------- The following commands were used to generate the test key pairs: development/tools/make_key testkey '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' development/tools/make_key platform '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' development/tools/make_key shared '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' development/tools/make_key media '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' development/tools/make_key cts_uicc_2021 '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com' signing using the openssl commandline (for boot/system images) -------------------------------------------------------------- 1. convert pk8 format key to pem format % openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem 2. create a signature using the pem format key % openssl dgst -binary -sha1 -sign testkey.pem FILE > FILE.sig extracting public keys for embedding ------------------------------------ dumpkey.jar is a Java tool that takes an x.509 certificate in PEM format as input and prints a C structure to standard output: $ java -jar out/host/linux-x86/framework/dumpkey.jar build/make/target/product/security/testkey.x509.pem {64,0xc926ad21,{1795090719,2141396315,950055447,2581568430,4268923165,1920809988,546586521,3498997798,1776797858,3740060814,1805317999,1429410244,129622599,1422441418,1783893377,1222374759,2563319927,323993566,28517732,609753416,1826472888,215237850,4261642700,4049082591,3228462402,774857746,154822455,2497198897,2758199418,3019015328,2794777644,87251430,2534927978,120774784,571297800,3695899472,2479925187,3811625450,3401832990,2394869647,3267246207,950095497,555058928,414729973,1136544882,3044590084,465547824,4058146728,2731796054,1689838846,3890756939,1048029507,895090649,247140249,178744550,3547885223,3165179243,109881576,3944604415,1044303212,3772373029,2985150306,3737520932,3599964420},{3437017481,3784475129,2800224972,3086222688,251333580,2131931323,512774938,325948880,2657486437,2102694287,3820568226,792812816,1026422502,2053275343,2800889200,3113586810,165549746,4273519969,4065247892,1902789247,772932719,3941848426,3652744109,216871947,3164400649,1942378755,3996765851,1055777370,964047799,629391717,2232744317,3910558992,191868569,2758883837,3682816752,2997714732,2702529250,3570700455,3776873832,3924067546,3555689545,2758825434,1323144535,61311905,1997411085,376844204,213777604,4077323584,9135381,1625809335,2804742137,2952293945,1117190829,4237312782,1825108855,3013147971,1111251351,2568837572,1684324211,2520978805,367251975,810756730,2353784344,1175080310}} This is called by build/make/core/Makefile to incorporate the OTA signing keys into the recovery image. converting to java keystore for Android Studio ---------------------------------------------- Suppose we want to convert shared.pk8 and shared.x509.pem to shared.keystore. $ openssl pkcs8 -inform DER -nocrypt \ -in build/make/target/product/security/shared.pk8 \ -out shared.pem $ openssl pkcs12 -export \ -in build/make/target/product/security/shared.x509.pem \ -inkey shared.pem -out shared.p12 \ -password pass:android -name AndroidDebugKey $ keytool -importkeystore -deststorepass android \ -destkeystore shared.keystore -srckeystore shared.p12 \ -srcstoretype PKCS12 -srcstorepass android The keystore can be used in build.gradle as follows. signingConfigs { shared { storeFile file("shared.keystore") storePassword "android" keyPassword "android" keyAlias "AndroidDebugKey" } } ================================================ FILE: target/product/security/bluetooth.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIGOzCCBCOgAwIBAgIUEiZapaWZVSter06CJMf2kHi8PIswDQYJKoZIhvcNAQEL BQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy b2lkMScwJQYDVQQDDB5jb20uYW5kcm9pZC5ibHVldG9vdGguc2VydmljZXMxIjAg BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMjIwMzE1MDAzNjAz WhgPNDc2MDAyMDkwMDM2MDNaMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs aWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9p ZDEQMA4GA1UECwwHQW5kcm9pZDEnMCUGA1UEAwweY29tLmFuZHJvaWQuYmx1ZXRv b3RoLnNlcnZpY2VzMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsVlq9pozUREGlb8u8Y0A fYwPs5OuavNx/EsX03aTjmAXUfSOMAewqzUXDIRjw8UQvOW63utaZ0go9osDPzNf VEftmGxW/AUC+HWGaLDQfCYO3ficPPOS7xpEhGZERNbnhvh5qX0NBt6mJygsfpOm RPThbi6Ig2Brxh1eqVYqRkTjhNFKD6gCd1PdMmUSF88xEYaZWvTkET89Zh38lLza 2x/wfNZmCSAVurNw1Kf9NQfYsaGHwMsjrvTyhG93TTYXzRBFzAO2WlBiw6R0tQr8 ZW5XCM9Yo6AS0KXiU0ZWwOXxhGdr38rNd7j9nZtpFwWmN1kgeb/vpEfq0Ylua9By uURnfJZu2K4TbFamuyjihItra2ZKOtFNPDeuggKMCkuZz6WU8FCoMEpnq5P2agxN OGAa7ynXdNzek98N3TGX8qtfEgCv6vyuM0gakJ6D9nM43nsCm1LkB/JA0CacWyRz ljaLL1C4S43azEOYyOOb94ITnkZCQGtH33kxzamyPLIZ37VF4+v6yTXySLBzOnhe Os5uBIDohVJuI838bLhZf8e5mIrnjiKwsmExXiQvgidbwvZKCz9n8YT4iUhWPx4F W+GPcivZsvsECcnJ2QURK1zhir5QuLS7ZbAth4kiEUxJ6ujF5jftE+L/ClK2LiY0 2IXWRCct8J1hfJZZx8lm3PUCAwEAAaNTMFEwHQYDVR0OBBYEFO5CgtQzKbTEd/Q9 rxK14a9BBwFZMB8GA1UdIwQYMBaAFO5CgtQzKbTEd/Q9rxK14a9BBwFZMA8GA1Ud EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAGrGS1zmaoARVq7qhoY+xzSc 1I/Tzf6vG6aHBC+CcIoSM2oqr6TGH+ADHAY6jhu/qzv1ij3gtoInAkBtkWvYsCIV eISPj8Qomcd8EIeW77p+ArKzS4HY5m1c/O4D/5rkl6c0exFq4Pdw9V8xyM98QtLd oj4xzzXUTPOIwkROHkj8otcML28m/MC0l/4b+flHnPqKFuLBjhxi9b/ZfwaXfjkx TcXpM3nPH8zN7kaJpS1fPW1IJyxJYvT022uK+afpezTmyS/50aOncUGjDJRw8CcO B88O8lpizDD3tD7P6jVOpRRJS4SnkVErbIn1xdWER6ubhnnycH7UmDVIx+vNd/t6 YDa377au8Za+LnbDPfV1+Og+RaJSEIjJgfYyqnjBxGdRGN21VbqJdRzo/eO4ZFd2 mGVtMosVr0jw4O8r60o9oMMWBTbFpxOI929QdcV+X1Lz8A8BZz0faXfZ2Z9usctu W2FtZge3tsJ07z7kuhNdbnm2yQVfd0FqiJsapUjlhgcdFVoDWPuqOfWAoG31ble6 eiNnxfjiCckPWyciIE6lw97nvavGjlUacH5qVG86hOWU7xyBgeQ0PH4e+Nxr50yU A0GMxni1gefZFG8qEPdNRuDT1QdqDGh/8Ea11GEUMXdAxk0UzqyAtLDr6MbwK6lV mqmeueFdogdjvQ3mXe94 -----END CERTIFICATE----- ================================================ FILE: target/product/security/cts_uicc_2021.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIECzCCAvOgAwIBAgIUHYLIIL60vWPD6aOBwZUcdbsae+cwDQYJKoZIhvcNAQEL BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu ZHJvaWQuY29tMB4XDTIxMDEyNjAwMjAyMVoXDTQ4MDYxMzAwMjAyMVowgZQxCzAJ BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlOMSHqBu0ihUDfFgwMfO pJtpyxHe0KKfHRndUQcYU/1v6/auy2YqkgKv+AraoukuU3gJeOiWoaqaWFNcm6md WfGRNT4oABhhNS43n5PI4NlLjI4yeUJJppZn5LPpc/8vZ0P8ZFE9CJmtckCh+hES BzqnxkCnq1PoxlcF3S/f8lOtd6ymaMDf3sYcePaoU8yTWFksl7EWRVwhBUIf7/r8 epbNiV14/aH2cQfHVfpf54TIdk7s0/ehVA70A5gQp7Utn6mY2zEJlMrTKWRqA/a5 oYiob3y+v2JWNcljHY6twwDOGwW7G0NWJVtaWj76Z3o9RpIhAglivhOrHTflIU3+ 2QIDAQABo1MwUTAdBgNVHQ4EFgQUZJ1oGb33n/OY+Mm8ykci4I6c9OcwHwYDVR0j BBgwFoAUZJ1oGb33n/OY+Mm8ykci4I6c9OcwDwYDVR0TAQH/BAUwAwEB/zANBgkq hkiG9w0BAQsFAAOCAQEASajvU0KCN2kfATPV95LQVE3N/URPi/lX9MfQptE54E+R 6dHwHQIwU/fBFapAHfGgrpwUZftJO+Bad2iu5s1IhTJ0Q5v0yHdvWfo4EzVeMzPV +/DWU786pPEomFkb9ZKhgVkFNPcbXlkUm/9HxRHPRTm8x+BE/75PKI+kh+pDmM+P 5v4W0qDKPgFzIY/D4F++gVyPZ3O+/GhunjsJozO+dvN+50FH6o/kBHm2+QqQNYPW f232F3CYtH4uWI0TkbwmSvVGW8iOqh330Cef5zqwSdOkzybUirXFsHUu1Zad1aLT t0mu6RgNEmX8efOQCcz2Z/on8lkIAxCBwLX7wkH5JA== -----END CERTIFICATE----- ================================================ FILE: target/product/security/media.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIEqDCCA5CgAwIBAgIJAPK5jmEjVyxOMA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe Fw0wODA0MTUyMzQwNTdaFw0zNTA5MDEyMzQwNTdaMIGUMQswCQYDVQQGEwJVUzET MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI hvcNAQEBBQADggENADCCAQgCggEBAK4lDFoW75f8KGmsZRsyF8w2ug6GlkFo1YoE n0DOhYZxI6P/tPbZScM88to6BcI+rKpX2AOImxdZvPWefG8hiQriUIW37VaqYmwJ ie+czTY2LKDo0blgP9TYModnkmzMCQxot3Wuf/MJNMw2nvKFWiZn3wxmf9DHz12O umVYBnNzA7tiRybquu37cvB+16dqs8uaOBxLfc2AmxQNiR8AITvkAfWNagamHq3D qcLxxlZyhbCa4JNCpm+kIer5Ot91c6AowzHXBgGrOvfMhAM+znx3KjpbhrDb6dd3 w6SKqYAe3O4ngVifRNnkETl5YAV2qZQQuoEJElna2YxsaP94S48CAQOjgfwwgfkw HQYDVR0OBBYEFMopPKqLwO0+VC7vQgWiv/K1fk11MIHJBgNVHSMEgcEwgb6AFMop PKqLwO0+VC7vQgWiv/K1fk11oYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPK5jmEjVyxOMAwGA1Ud EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAITelRbV5KhyF6c9qEhwSPUzc6X3 M/OQ1hvfPMnlJRYlv8qnwxWcriddFyqa4eh21UWBJ6xUL2gpDdUQwAKdj1Hg7hVr e3tazbOUJBuOx4t05cQsXK+uFWyvW9GZojonUk2gct6743hGSlM2MLDk0P+34I7L cB+ttjecdEZ/bgDG7YiFlTgHkgOHVgB4csjjAHr0I6V6LKs6KChptkxLe9X8GH0K fiQVll1ark4Hpt91G0p16Xk8kYphK4HNC2KK7gFo3ETkexDTWTJghJ1q321yfcJE RMIh0/nsw2jK0HmZ8rgQW8HyDTjUEGbMFBHCV6lupDSfV0ZWVQfk6AIKGoE= -----END CERTIFICATE----- ================================================ FILE: target/product/security/networkstack.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIF3DCCA8SgAwIBAgIJAPxssNim/dFoMA0GCSqGSIb3DQEBCwUAMIGBMQswCQYD VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEhMB8GA1UE AwwYY29tLmFuZHJvaWQubmV0d29ya3N0YWNrMCAXDTE5MDIxMjAxNDYyMFoYDzQ3 NTcwMTA4MDE0NjIwWjCBgTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju aWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAO BgNVBAsMB0FuZHJvaWQxITAfBgNVBAMMGGNvbS5hbmRyb2lkLm5ldHdvcmtzdGFj azCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALtx9RN/8LLXV6zCyj03 jg+N4RCQ1crz1J4xTTXCg7d4sC15LY66RANkypcJhUQWYPC8AK+8Y91hGxv1GtKK Ht0h4ASPVIuA+L0RPiVoKCL1fauCc6+vEsZNGaDGviOPPmbdx5sQ/ZJpMePuYKe/ YYZE2jwsT8QoE51F0nvtp/5F4wB1tJPq1uwBzdVdkxwKZX4uWXQspjK23DhCot63 0iRDyAkpHXpUkgOuauNWWCpMoj8w8FScTshAinUnjpXGnoOQrVKAvO+u9vEwmkG9 nzv7XRLcp+eexv1oSBk/qatygiSIe0+T6YXsfL9kAbDoY6S5HAXQRvBA/pVABLFk WVT8tBFM7h6LZLR9cZoZ70wAHLGD9/PhZuQ/VtaAR8NEDaNP31KdRCdLiy9q+zRQ ka2K1Lk71cVdUihqXTwVdGXbjd9i5822sQ+xiIgEav3SY65vISXZBldZx+QvhhCm dG7b3FR9QwFhLu7Dw8vRJN7OzI04sg5zsT8k7nyhOpjF9h8MgbB9K1GXSbwry54J Sa72wRij6BJearV/zka7CRpmdA4Qsxx0C4kZAMDs2pzGnstPM2mZixdRBt0KT/1w JOt+df7dGlsTHQuytAxjSR48+GuJV7IVIbOpbtE3alGmrGl4ZrAlbe4bzZq5oYi/ TO2AtZpfJMLamlXrew5QIRbjAgMBAAGjUzBRMB0GA1UdDgQWBBSTg8ks+/CZ1cR7 DDZX2GIqCEty4TAfBgNVHSMEGDAWgBSTg8ks+/CZ1cR7DDZX2GIqCEty4TAPBgNV HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBqBQE4L94qa49wxgzRuO5P eIcYwoixcCWO86liMLZQBWUNakxCpZqXst3sUCQT57Q4+9BgNj10t0ojI4Kn93/T 2jTjj3n60DWotHLFz/NlgYoBGNh/oeMcx+1L79J2KHYMKQmAw8w7f/DP0Bt1/x/M g+mBtbJaVNhbaKgEJKwmAV+zpMdUlppxF0wLwoP2yIGR3O1gniRfWTj/0K15kZji 0L9jQiIcGwpdMy7S//xmiYLKu8t9O2MP+EduXISsCtN635IkA1IAA5+V7B+pW/g3 lsDomGE1zuLcrvGQskmFWn5zl9SgvxfqY9l4WJxrSBGKOB//vXkMRNgCM+LjUpKj tVM8o/LMFz+Fz5BK3+Lk4hg9weug664HuDmoH/G8kuKSVQlXyFma8h6cBJe5I0zj RfP1CLHMhyqlXdtedzxcfdZXe5qLba7SCuH/S4IG/Z9cj1oiuhmAvvAa5vyyZZuX rVuYX6gcAZ/+AI3dnIEwwG/GAyshScIgn8Q4p+jDsgzgNlCtMcTuSPFpd3oK4YK3 LKMbgVQPYfFn2Net9Pa7IzD/XCQDckUADYFywSq11apYkLixLbDw5yliZOtm5/lx TDEARkn7S4ZABfnEPIDbP23lL9RNbiA2v+f1gHFW7Vq1kdBv1ruTukM06ic5r4tB 7SaGRU5gtmbRBzi7e6iAAQ== -----END CERTIFICATE----- ================================================ FILE: target/product/security/nfc.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIF2DCCA8CgAwIBAgIUC94q348hFaPm2jow3R84ZjNFc3EwDQYJKoZIhvcNAQELBQAwfDELMAkG A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDAS BgNVBAoTC0dvb2dsZSBJbmMuMRAwDgYDVQQLEwdBbmRyb2lkMRgwFgYDVQQDDA9jb21fYW5kcm9p ZF9uZmMwIBcNMjMxMTAxMjEzNzE1WhgPMjA1MzExMDEyMTM3MTVaMHwxCzAJBgNVBAYTAlVTMRMw EQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29n bGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEYMBYGA1UEAwwPY29tX2FuZHJvaWRfbmZjMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArpgwKyLDl8M3KRb1Fxs3P2mnt81sB3uGZs44R6C6 CwFhiiACOmEQBcm79BUKBMrE9dUbyOL/GKluNzD026UsDE+N2wDQ8siTxaljDAwhZBpurhOu4uH8 BKJOzoczAlJFMHpFIMCKQXwotMjT93BuhlSo024Q5QDd2j7Gajk21TkkyQNlBOiyEpKkrRPBuArw 2knqhuX+nLYkJ5roANaJVDsiKMDG/mKnjwAndrgVbBiKaOdfuRd+pJleN3LUkAfYHHBqlOJnPGSI jfYK+9TjsIEYVEOb4SMI3CbWwHfOdEIBgz3IPqMtamEnbZHNlfVWURTNGAF2co+DF3TDGDEjraK4 R5pXDk/W+4Ex77wQPCIT+d981zkbTpgsPXvZmsBzCYMw6tYksPj86fSVJUrJhlibDk4YHVFsF7OK arNf044yPhZ+WUIDqWJ6GB0GU8LWGbbe8iaP0ro9Q1DYgYc6buYWIcX81XZO+hHgWtUb2rNqIUsp /4DmT1vgz7TiMWcY7pjrHlNHtVf4jC+OU2c+p8u4XUGQxdIKGgZSoHldtAcnwqGuIpat9lS+gtVl vJUp8w3Z2gv4q/bBVZ3NNasA1d3HXVQUWiwszcjiVvoSRa/AlMVUGureGRbsiKsyHisYp9rxk1DB dPS9h7tMs/5rV6RM2nZfdfQr71zX9ieSoz0CAwEAAaNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4E FgQU9v9SL0QIU9fq7aB70/jqVBLmZJUwHwYDVR0jBBgwFoAU9v9SL0QIU9fq7aB70/jqVBLmZJUw DQYJKoZIhvcNAQELBQADggIBAExt2/NFt+2IhC5+/cgi8hzvwZKQyml1MQ9pjrkfQy0JGzGTOPDr +NPuT5jh/SOfGzdBsGVs3hvK7hFvXsFEWwVQaDOkKhcruks+g7FxldhXC2z9iKgjNrOeUXoE7SiE zXA/p1KiBcRL3MSMbg/iQjQVxlJky4BDo39SoEgDeL9+i7L4cBwZQ2LBBLPIOdE7G/cG1Q6UE+KN /mnz0kk3+FIg0q4szXDxH+o2V4ZYHSOy8P6TBW8gEQ71RGOnh0wtaTIx2RD/zqJAi+BzWMLsC636 TNMmqKassG4MH445ul2w0ZzOClJ4gkl1e7dtK7/Kes4kcLOI/i4JHLOcydEqum+t8gQtMYyGM1Kv mVpC3hEv2pYwFfhg8hg31MljZmLD761kLOLfw98N350h6dNdQ0jci/3rqbbjVinVQoQSVEzJcA9Q gQhRLKHiO7oRmht6ilRLFtGZd/PwIMWMNqksTfVM5frMIIZXdfew+efHIJ7X+ZgJu3tGWcbFYFte K/BbmPLnp3aAGg/wwU1dqwCANf53oUc4ZzqRm9eovlVsrFiRM/DGt2/t4ujorU6Uwwt2+n05QU7b 7PXhc7bTP6adUWMNMxSNIPo6wHmsTb2pCg+K5LuNMFJzXcoI3uBW9Qu4M/tLRv4kRKZzphqUbX+e /5hW2myw2BvbdwWFrz6XBgkz -----END CERTIFICATE----- ================================================ FILE: target/product/security/platform.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIEqDCCA5CgAwIBAgIJALOZgIbQVs/6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe Fw0wODA0MTUyMjQwNTBaFw0zNTA5MDEyMjQwNTBaMIGUMQswCQYDVQQGEwJVUzET MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI hvcNAQEBBQADggENADCCAQgCggEBAJx4BZKsDV04HN6qZezIpgBuNkgMbXIHsSAR vlCGOqvitV0Amt9xRtbyICKAx81Ne9smJDuKgGwms0sTdSOkkmgiSQTcAUk+fArP GgXIdPabA3tgMJ2QdNJCgOFrrSqHNDYZUer3KkgtCbIEsYdeEqyYwap3PWgAuer9 5W1Yvtjo2hb5o2AJnDeoNKbf7be2tEoEngeiafzPLFSW8s821k35CjuNjzSjuqtM 9TNxqydxmzulh1StDFP8FOHbRdUeI0+76TybpO35zlQmE1DsU1YHv2mi/0qgfbX3 6iANCabBtJ4hQC+J7RGQiTqrWpGA8VLoL4WkV1PPX8GQccXuyCcCAQOjgfwwgfkw HQYDVR0OBBYEFE/koLPdnLop9x1yh8Tnw48ghsKZMIHJBgNVHSMEgcEwgb6AFE/k oLPdnLop9x1yh8Tnw48ghsKZoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJALOZgIbQVs/6MAwGA1Ud EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFclUbjZOh9z3g9tRp+G2tZwFAAp PIigzXzXeLc9r8wZf6t25iEuVsHHYc/EL9cz3lLFCuCIFM78CjtaGkNGBU2Cnx2C tCsgSL+ItdFJKe+F9g7dEtctVWV+IuPoXQTIMdYT0Zk4u4mCJH+jISVroS0dao+S 6h2xw3Mxe6DAN/DRr/ZFrvIkl5+6bnoUvAJccbmBOM7z3fwFlhfPJIRc97QNY4L3 J17XOElatuWTG5QhdlxJG3L7aOCA29tYwgKdNHyLMozkPvaosVUz7fvpib1qSN1L IC7alMarjdW4OZID2q4u1EYjLk/pvZYTlMYwDlE448/Shebk5INTjLixs1c= -----END CERTIFICATE----- ================================================ FILE: target/product/security/sdk_sandbox.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIECzCCAvOgAwIBAgIUMWJGQnrJU7zBEpPqv63u2HOlib0wDQYJKoZIhvcNAQEL BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu ZHJvaWQuY29tMB4XDTIxMTEwMjE3MDIxNFoXDTQ5MDMyMDE3MDIxNFowgZQxCzAJ BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA09j3dyTxv8ojb4sXjrWX smXTYEez/u6X6po8+mWXp1xl1Y9xjYrxZROIE1MJL8aay8iYJihqx7RBWTPJYtYZ TLElA3dyQuMgDIKtlQR3QAMRoc2IKrkfcIboEs71xl78EnTSQfRJTUEFvNigzjfB e3JVtNDC9BR/33Iv9oNED84qW9C54h4TWHLyvo75unzPQUGS6uEIhhHa/8ynZZQW YEd0NwAQNqbcMdbN8Bn6sRRCidEOIPd8Uu8DtIofLi7/YMo4CH1Q5f5UQbtPtqU2 m8fjQN9WYzMazvWltRE+HYDH9YnXCLAsVicNdmFhAlXri15nG2AiRnSrHu/panAc 6wIDAQABo1MwUTAdBgNVHQ4EFgQU3F5r2DhJbRfkJKuqs1hjP/0dCUEwHwYDVR0j BBgwFoAU3F5r2DhJbRfkJKuqs1hjP/0dCUEwDwYDVR0TAQH/BAUwAwEB/zANBgkq hkiG9w0BAQsFAAOCAQEAwQQ8/D3f/WS5cwqcsFpT+Qzik9yTu53nsXz/pBDSbeM3 zX1RCejXsmXhPjN7cu0uJYlrIuArOagHSC5pDci6GzcwunnnkRazSAmTpHLSRgeb cLgKHLCph9sulI1r82x9upF47zLlbfkTrtGJryej+yWJ2Ne8irJIPeNR0z0sTBWJ 2Ngg55ezFWj3mihzw4Z6YU9txJB7Gj9eNYXdcubjoNs2mSU/6dR+HwJtD64FuH3x QLGMZscizCN8N6b5xayjwPsszQhaHI4iR4oGJ9prbDd0JoylwWr2LrQhYuWQCn20 cG5YhrtZshj6f1eGV1TDYd8xziapilqwzrchARvP8g== -----END CERTIFICATE----- ================================================ FILE: target/product/security/shared.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIEqDCCA5CgAwIBAgIJAPKnM5a9OHZ6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe Fw0wODA3MjMyMTU3NTlaFw0zNTEyMDkyMTU3NTlaMIGUMQswCQYDVQQGEwJVUzET MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI hvcNAQEBBQADggENADCCAQgCggEBAMjC2/0JSi30XD/xoy7SGAXscvxY0BeXG9D2 tSwmLXCBnRkZZ+FY39Oix/Gz4OgM5UXXnShIIgIR64bw/YMS03tCDBE3UMyUYYro cvSIZGO9xGJ8qgwEg8hkk+NRVXEXAzi/3MTNat3RwKLzX1zyTtPkBDo+WOKwXmZM zeEry2dzX9bfEknDaeYlQrwKRynlORf1w4/6UtF7c8nHN5jdsY7UgVkIdVR+Zr/F 2spMJabrlg7ZaSNwnaMCumRstJazJehsXIsuejN3srvkx88zJUKRFj9okVKsCIVQ yDxQj0v1rfCu1aLcoFg/mrCtF2UNt+6ksj/bRYhVR9D+q3IYOIkCAQOjgfwwgfkw HQYDVR0OBBYEFMtMfizbs/CtqY2reZaNFy6dux7RMIHJBgNVHSMEgcEwgb6AFMtM fizbs/CtqY2reZaNFy6dux7RoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPKnM5a9OHZ6MAwGA1Ud EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAECo0JaZeVnpF6NsRCRra6wrrgVD fs2JeUEY94NHIDUtHG+KObCGmUL02mWYH6opUdM5cRKewZIdeVZxxSfW4knyUoKf r1tZExAxHi3gllANVorUEUplbcNKjG9hBFOvwep5ktukqns/hUOm41wHKN53/pfu rIN3H9DskPjkRJQ07gtgRXg+cMei5GAkkmDgA892CNw1Kkye9wbe9LJgUOl4ri// 16MyN4cBSRXrPMh0/MeprpMId8XIx9HC4qjuhjyJGA0YVc7bpADnukPMyqckPTl+ fA6Ojk19T5K2u+rUnAzwGAae3coufi+0Zo2J2715UNDNJUGA+h6q/CpVb4Q= -----END CERTIFICATE----- ================================================ FILE: target/product/security/testkey.x509.pem ================================================ -----BEGIN CERTIFICATE----- MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4 wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy 4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe +ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX 31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0= -----END CERTIFICATE----- ================================================ FILE: target/product/sysconfig/Android.bp ================================================ // Copyright (C} 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"}; // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], } prebuilt_etc { name: "preinstalled-packages-platform-aosp-product.xml", product_specific: true, sub_dir: "sysconfig", src: "preinstalled-packages-platform-aosp-product.xml", } prebuilt_etc { name: "preinstalled-packages-platform-full-base.xml", sub_dir: "sysconfig", src: "preinstalled-packages-platform-full-base.xml", } prebuilt_etc { name: "preinstalled-packages-platform-generic-system.xml", sub_dir: "sysconfig", src: "preinstalled-packages-platform-generic-system.xml", } prebuilt_etc { name: "preinstalled-packages-platform-handheld-product.xml", product_specific: true, sub_dir: "sysconfig", src: "preinstalled-packages-platform-handheld-product.xml", } prebuilt_etc { name: "preinstalled-packages-platform-handheld-system.xml", sub_dir: "sysconfig", src: "preinstalled-packages-platform-handheld-system.xml", } prebuilt_etc { name: "preinstalled-packages-platform-telephony-product.xml", product_specific: true, sub_dir: "sysconfig", src: "preinstalled-packages-platform-telephony-product.xml", } prebuilt_etc { name: "initial-package-stopped-states-aosp.xml", product_specific: true, sub_dir: "sysconfig", src: "initial-package-stopped-states-aosp.xml", } ================================================ FILE: target/product/sysconfig/initial-package-stopped-states-aosp.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-full-base.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-generic-system.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-handheld-system.xml ================================================ ================================================ FILE: target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml ================================================ ================================================ FILE: target/product/telephony.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # All modules for telephony $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk) ================================================ FILE: target/product/telephony_product.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is the list of modules that are specific to products that have telephony # hardware, and install to the product partition. # /product packages PRODUCT_PACKAGES += \ Dialer \ ImsServiceEntitlement \ preinstalled-packages-platform-telephony-product.xml ================================================ FILE: target/product/telephony_system.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is the list of modules that are specific to products that have telephony # hardware, and install on the system partition. PRODUCT_PACKAGES := \ ONS \ CarrierDefaultApp \ CallLogBackup \ com.android.cellbroadcast \ CellBroadcastLegacyApp \ PRODUCT_COPY_FILES := \ ================================================ FILE: target/product/telephony_system_ext.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is the list of modules that are specific to products that have telephony # hardware, and install to the system_ext partition. # /system_ext packages PRODUCT_PACKAGES += \ CarrierConfig \ EmergencyInfo \ PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \ hwservicemanager \ android.hidl.allocator@1.0-service \ ================================================ FILE: target/product/telephony_vendor.mk ================================================ # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is the list of modules that are specific to products that have telephony # hardware, and install outside the system partition. # /vendor packages PRODUCT_PACKAGES := \ rild \ ================================================ FILE: target/product/updatable_apex.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt # and CtsShimPrivPrebuilt since they are packaged inside the APEX. PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt PRODUCT_SYSTEM_PROPERTIES := ro.apex.updatable=true # Use compressed apexes in pre-installed partitions. # Note: this doesn't mean that all pre-installed apexes will be compressed. # Whether an apex is compressed or not is controlled at apex Soong module # via compresible property. PRODUCT_COMPRESSED_APEX := true ================================================ FILE: target/product/userspace_reboot.mk ================================================ # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # DEPRECATED! Do not inherit this. ================================================ FILE: target/product/virtual_ab_ota/OWNERS ================================================ zhangkelvin@google.com dvander@google.com akailash@google.com ================================================ FILE: target/product/virtual_ab_ota/README.md ================================================ # Virtual A/B makefiles Devices that uses Virtual A/B must inherit from one of the makefiles in this directory. ## Structure ``` launch.mk |- retrofit.mk |- plus_non_ab.mk launch_with_vendor_ramdisk.mk |- compression.mk compression_retrofit.mk ``` ================================================ FILE: target/product/virtual_ab_ota/android_t_baseline.mk ================================================ # # Copyright (C) 2022 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file should be used only for T launching devices. We maintain # this file just for backward compatibility for T launch devices # so that build doesn't break. # # All U+ launching devices should instead use vabc_features.mk. $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk) ================================================ FILE: target/product/virtual_ab_ota/compression.mk ================================================ # # Copyright (C) 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk) PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true # Optional assignment. On low memory devices, disabling io_uring can relieve cpu and memory # pressure during an OTA. PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true # Enabling this property, will improve OTA install time # but will use an additional CPU core # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true PRODUCT_VIRTUAL_AB_COMPRESSION := true PRODUCT_PACKAGES += \ snapuserd.vendor_ramdisk \ snapuserd \ snapuserd.recovery ================================================ FILE: target/product/virtual_ab_ota/compression_retrofit.mk ================================================ # # Copyright (C) 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true PRODUCT_VIRTUAL_AB_COMPRESSION := true # For devices that are not GKI-capable (eg do not have vendor_boot), # snapuserd.ramdisk is included rather than snapuserd.vendor_ramdisk. # When using virtual_ab_ota_compression_retrofit.mk, either # virtual_ab_ota.mk or virtual_ab_ota_retrofit.mk must be inherited # as well. PRODUCT_PACKAGES += \ snapuserd.ramdisk \ snapuserd \ snapuserd.recovery ================================================ FILE: target/product/virtual_ab_ota/compression_with_xor.mk ================================================ # # Copyright (C) 2021 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk) PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true ================================================ FILE: target/product/virtual_ab_ota/launch.mk ================================================ # # Copyright (C) 2019 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PRODUCT_VIRTUAL_AB_OTA := true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true PRODUCT_PACKAGES += e2fsck_ramdisk ================================================ FILE: target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk ================================================ # # Copyright (C) 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Devices launching with Virtual A/B and has a vendor_boot partition is # preferred to inherit from this makefile instead of launch.mk. PRODUCT_VIRTUAL_AB_OTA := true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true PRODUCT_PACKAGES += \ linker.vendor_ramdisk \ e2fsck.vendor_ramdisk \ fsck.f2fs.vendor_ramdisk \ ================================================ FILE: target/product/virtual_ab_ota/plus_non_ab.mk ================================================ # # Copyright (C) 2020 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk) PRODUCT_OTA_FORCE_NON_AB_PACKAGE := true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.allow_non_ab=true ================================================ FILE: target/product/virtual_ab_ota/retrofit.mk ================================================ # # Copyright (C) 2019 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk) PRODUCT_VIRTUAL_AB_OTA_RETROFIT := true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.retrofit=true ================================================ FILE: target/product/virtual_ab_ota/vabc_features.mk ================================================ # # Copyright (C) 2022 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This file enables baseline features, such as io_uring, # userspace merge, etc. But sets compression method to none. # This .mk file also removes snapuserd from vendor ramdisk, # as T launching devices will have init_boot which has snapuserd # in generic ramdisk. # # T and U launching devices should include this .mk file, and configure # compression algorithm by setting # PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD to lz4, gz or brotli. Complete # set of supported algorithms can be found in # system/core/fs_mgr/libsnapshot/cow_writer.cpp PRODUCT_VIRTUAL_AB_OTA := true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true # Optional assignments, low memory devices may benefit from overriding these. PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled?=true # Low memory device configurations. If memory usage and cpu utilization is # a bottleneck during OTA, the below configurations can be added to a # device's .mk file improve performance for low mem devices. # # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.read_ahead_size=16 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.o_direct.enabled=true # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.merge_thread_priority=19 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.worker_thread_priority=0 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_worker_threads=3 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_merge_threads=1 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.num_verify_threads=1 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.cow_op_merge_size=16 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_threshold_size=1073741824 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_block_size=1048576 # Enabling this property, will improve OTA install time # but will use an additional CPU core # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true ifndef PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536 endif PRODUCT_VIRTUAL_AB_COMPRESSION := true PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD ?= none PRODUCT_PACKAGES += \ snapuserd \ ================================================ FILE: target/product/window_extensions.mk ================================================ # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Extension of window_extensions_base.mk to enable the activity embedding # feature for all apps by default. All large screen devices must inherit # this in build. Optional for other form factors. # # Indicated whether the Activity Embedding feature should be guarded by # Android 15 to avoid app compat impact. # If true (or not set), the feature is only enabled for apps with target # SDK of Android 15 or above. # If false, the feature is enabled for all apps. PRODUCT_PRODUCT_PROPERTIES += \ persist.wm.extensions.activity_embedding_guard_with_android_15=false ================================================ FILE: target/product/window_extensions_base.mk ================================================ # # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # The base version of window_extensions.mk to be included on all non-wearable # devices. Devices that don't support multi-window can choose to drop this. # # Note: by default the Activity Embedding feature is guarded by app's # targetSDK on Android 15 to avoid app compat impact. # # Large screen devices must inherit window_extensions.mk to enable the # Activity Embedding feature for all apps. # /system_ext packages PRODUCT_PACKAGES += \ androidx.window.extensions \ androidx.window.sidecar # properties PRODUCT_PRODUCT_PROPERTIES += \ persist.wm.extensions.enabled=true ================================================ FILE: teams/Android.bp ================================================ // // Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // DON'T ADD NEW RULES HERE. For more details refer to // go/new-android-ownership-model package { default_applicable_licenses: ["Android-Apache-2.0"], } team { name: "trendy_team_qmc_pss", // go/trendy/manage/engineers/6342544841375744 trendy_team_id: "6342544841375744", } team { name: "trendy_team_cpu_team", // go/trendy/manage/engineers/5119059747307520 trendy_team_id: "5119059747307520", } team { name: "trendy_team_pwg_mobile", // go/trendy/manage/engineers/4869274588315648 trendy_team_id: "4869274588315648", } team { name: "trendy_team_pce_weu", // go/trendy/manage/engineers/5205725968891904 trendy_team_id: "5205725968891904", } team { name: "trendy_team_peeps_t_pgm_android_engprod", // go/trendy/manage/engineers/6288284960358400 trendy_team_id: "6288284960358400", } team { name: "trendy_team_appsearch", // go/trendy/manage/engineers/5075661716815872 trendy_team_id: "5075661716815872", } team { name: "trendy_team_shayba_team", // go/trendy/manage/engineers/6213135020228608 trendy_team_id: "6213135020228608", } team { name: "trendy_team_pixel_system_software", // go/trendy/manage/engineers/4856005120622592 trendy_team_id: "4856005120622592", } team { name: "trendy_team_platform_enabler_framework_make_pixel_", // go/trendy/manage/engineers/5893944097243136 trendy_team_id: "5893944097243136", } team { name: "trendy_team_pixel_system_sw_power_sw", // go/trendy/manage/engineers/6703184655286272 trendy_team_id: "6703184655286272", } team { name: "trendy_team_marvinpaul_team", // go/trendy/manage/engineers/4800689692901376 trendy_team_id: "4800689692901376", } team { name: "trendy_team_interactive_tv", // go/trendy/manage/engineers/6150577853661184 trendy_team_id: "6150577853661184", } team { name: "trendy_team_pixel_system_sw_sensor_framework", // go/trendy/manage/engineers/5005310567284736 trendy_team_id: "5005310567284736", } team { name: "trendy_team_wsd_arch", // go/trendy/manage/engineers/6173769806512128 trendy_team_id: "6173769806512128", } team { name: "trendy_team_lanechr_team", // go/trendy/manage/engineers/5674594204811264 trendy_team_id: "5674594204811264", } team { name: "trendy_team_test_eng_android_for_work", // go/trendy/manage/engineers/5909887015845888 trendy_team_id: "5909887015845888", } team { name: "trendy_team_camera_app", // go/trendy/manage/engineers/5216644934533120 trendy_team_id: "5216644934533120", } team { name: "trendy_team_vamaraju_team", // go/trendy/manage/engineers/5150510960771072 trendy_team_id: "5150510960771072", } team { name: "trendy_team_android_media_audio_framework", // go/trendy/manage/engineers/5823575353065472 trendy_team_id: "5823575353065472", } team { name: "trendy_team_superglue", // go/trendy/manage/engineers/5211667882999808 trendy_team_id: "5211667882999808", } team { name: "trendy_team_display_framework", // go/trendy/manage/engineers/6035600925163520 trendy_team_id: "6035600925163520", } team { name: "trendy_team_ananthak_team", // go/trendy/manage/engineers/6706043301298176 trendy_team_id: "6706043301298176", } team { name: "trendy_team_qmc_pqm", // go/trendy/manage/engineers/4715267632267264 trendy_team_id: "4715267632267264", } team { name: "trendy_team_search_allapps", // go/trendy/manage/engineers/4926160670195712 trendy_team_id: "4926160670195712", } team { name: "trendy_team_communal", // go/trendy/manage/engineers/6380669942530048 trendy_team_id: "6380669942530048", } team { name: "trendy_team_nova", // go/trendy/manage/engineers/5418955074043904 trendy_team_id: "5418955074043904", } team { name: "trendy_team_deprecated_framework_o_o", // go/trendy/manage/engineers/5999497213509632 trendy_team_id: "5999497213509632", } team { name: "trendy_team_android_go", // go/trendy/manage/engineers/6543205713444864 trendy_team_id: "6543205713444864", } team { name: "trendy_team_wear_wear_frameworks", // go/trendy/manage/engineers/5138392408817664 trendy_team_id: "5138392408817664", } team { name: "trendy_team_ssd_sensor", // go/trendy/manage/engineers/5084703539200000 trendy_team_id: "5084703539200000", } team { name: "trendy_team_dontek_team", // go/trendy/manage/engineers/5746076285042688 trendy_team_id: "5746076285042688", } team { name: "trendy_team_carrier_field_test", // go/trendy/manage/engineers/6409766640975872 trendy_team_id: "6409766640975872", } team { name: "trendy_team_pmw_standards", // go/trendy/manage/engineers/6428806822526976 trendy_team_id: "6428806822526976", } team { name: "trendy_team_build_infra", // go/trendy/manage/engineers/4516184164433920 trendy_team_id: "4516184164433920", } team { name: "trendy_team_qmc_gft", // go/trendy/manage/engineers/5454139446132736 trendy_team_id: "5454139446132736", } team { name: "trendy_team_android_storage", // go/trendy/manage/engineers/6301594936049664 trendy_team_id: "6301594936049664", } team { name: "trendy_team_pixel_mobile_wireless", // go/trendy/manage/engineers/4821918175887360 trendy_team_id: "4821918175887360", } team { name: "trendy_team_camera_from_google", // go/trendy/manage/engineers/4799694104854528 trendy_team_id: "4799694104854528", } team { name: "trendy_team_pixel_connectivity_settings", // go/trendy/manage/engineers/5622496450871296 trendy_team_id: "5622496450871296", } team { name: "trendy_team_androidbugtool_abt_", // go/trendy/manage/engineers/6531817781493760 trendy_team_id: "6531817781493760", } team { name: "trendy_team_wear_wear_security", // go/trendy/manage/engineers/6325699325362176 trendy_team_id: "6325699325362176", } team { name: "trendy_team_pascallouis_team", // go/trendy/manage/engineers/5111238276317184 trendy_team_id: "5111238276317184", } team { name: "trendy_team_android_camera_ecosystem_enabling", // go/trendy/manage/engineers/4529290269327360 trendy_team_id: "4529290269327360", } team { name: "trendy_team_calendar", // go/trendy/manage/engineers/6719127573889024 trendy_team_id: "6719127573889024", } team { name: "trendy_team_cgc", // go/trendy/manage/engineers/4590315499061248 trendy_team_id: "4590315499061248", } team { name: "trendy_team_diagnostic_tool", // go/trendy/manage/engineers/4689924564746240 trendy_team_id: "4689924564746240", } team { name: "trendy_team_pixel_camera_system_software", // go/trendy/manage/engineers/6386525306486784 trendy_team_id: "6386525306486784", } team { name: "trendy_team_credential_manager", // go/trendy/manage/engineers/5276403428655104 trendy_team_id: "5276403428655104", } team { name: "trendy_team_wear_wti_wear_tools_and_infra_", // go/trendy/manage/engineers/6225571306438656 trendy_team_id: "6225571306438656", } team { name: "trendy_team_pixel_biometrics_face", // go/trendy/manage/engineers/5028705926742016 trendy_team_id: "5028705926742016", } team { name: "trendy_team_location_time", // go/trendy/manage/engineers/4883807600017408 trendy_team_id: "4883807600017408", } team { name: "trendy_team_android_hardware_backed_security", // go/trendy/manage/engineers/6398595556343808 trendy_team_id: "6398595556343808", } team { name: "trendy_team_play_newsstand", // go/trendy/manage/engineers/5171015201980416 trendy_team_id: "5171015201980416", } team { name: "trendy_team_deprecated_framework_jaggies", // go/trendy/manage/engineers/5753206608887808 trendy_team_id: "5753206608887808", } team { name: "trendy_team_make_pixel_tpgm", // go/trendy/manage/engineers/6061069864665088 trendy_team_id: "6061069864665088", } team { name: "trendy_team_make_transformer", // go/trendy/manage/engineers/6224539427438592 trendy_team_id: "6224539427438592", } team { name: "trendy_team_wittrock_team", // go/trendy/manage/engineers/5707412083474432 trendy_team_id: "5707412083474432", } team { name: "trendy_team_wear_wear_android_companion_sdk", // go/trendy/manage/engineers/4864923637022720 trendy_team_id: "4864923637022720", } team { name: "trendy_team_assistant_sysui_integration", // go/trendy/manage/engineers/4884282575060992 trendy_team_id: "4884282575060992", } team { name: "trendy_team_things", // go/trendy/manage/engineers/5206199574069248 trendy_team_id: "5206199574069248", } team { name: "trendy_team_wsd_w13", // go/trendy/manage/engineers/5612469120532480 trendy_team_id: "5612469120532480", } team { name: "trendy_team_iqbalasif_team", // go/trendy/manage/engineers/4912049813094400 trendy_team_id: "4912049813094400", } team { name: "trendy_team_biometric_security", // go/trendy/manage/engineers/5797911960649728 trendy_team_id: "5797911960649728", } team { name: "trendy_team_silberst_team", // go/trendy/manage/engineers/5710892584042496 trendy_team_id: "5710892584042496", } team { name: "trendy_team_pmw_telephony", // go/trendy/manage/engineers/6029121444151296 trendy_team_id: "6029121444151296", } team { name: "trendy_team_zzz", // go/trendy/manage/engineers/6351340934397952 trendy_team_id: "6351340934397952", } team { name: "trendy_team_lite_team", // go/trendy/manage/engineers/5647925813346304 trendy_team_id: "5647925813346304", } team { name: "trendy_team_gms_core", // go/trendy/manage/engineers/5735614422843392 trendy_team_id: "5735614422843392", } team { name: "trendy_team_dialer_make_pixel_", // go/trendy/manage/engineers/5126396509978624 trendy_team_id: "5126396509978624", } team { name: "trendy_team_pixel_system_sw_color", // go/trendy/manage/engineers/5489236125581312 trendy_team_id: "5489236125581312", } team { name: "trendy_team_fwk_nfc", // go/trendy/manage/engineers/5962312512864256 trendy_team_id: "5962312512864256", } team { name: "trendy_team_srajkumar_team", // go/trendy/manage/engineers/5170053894012928 trendy_team_id: "5170053894012928", } team { name: "trendy_team_in_market_tpm", // go/trendy/manage/engineers/5352549888196608 trendy_team_id: "5352549888196608", } team { name: "trendy_team_leannogasawara_team", // go/trendy/manage/engineers/4905467198472192 trendy_team_id: "4905467198472192", } team { name: "trendy_team_zurikemp_team", // go/trendy/manage/engineers/4559796603879424 trendy_team_id: "4559796603879424", } team { name: "trendy_team_android_telemetry_infra", // go/trendy/manage/engineers/5295809771732992 trendy_team_id: "5295809771732992", } team { name: "trendy_team_system_ui_sensors", // go/trendy/manage/engineers/5647653492621312 trendy_team_id: "5647653492621312", } team { name: "trendy_team_windowing_animations_transitions", // go/trendy/manage/engineers/4803040337362944 trendy_team_id: "4803040337362944", } team { name: "trendy_team_deprecated_framework_jjaggi", // go/trendy/manage/engineers/6471742270898176 trendy_team_id: "6471742270898176", } team { name: "trendy_team_accessibility_hearing_aids", // go/trendy/manage/engineers/4661226340253696 trendy_team_id: "4661226340253696", } team { name: "trendy_team_performance", // go/trendy/manage/engineers/5842000521625600 trendy_team_id: "5842000521625600", } team { name: "trendy_team_cloud_android", // go/trendy/manage/engineers/5980255760023552 trendy_team_id: "5980255760023552", } team { name: "trendy_team_visual_design", // go/trendy/manage/engineers/4504161399734272 trendy_team_id: "4504161399734272", } team { name: "trendy_team_wilkinsonclay_team", // go/trendy/manage/engineers/5680997128634368 trendy_team_id: "5680997128634368", } team { name: "trendy_team_tccyp_nadiae", // go/trendy/manage/engineers/6556518831652864 trendy_team_id: "6556518831652864", } team { name: "trendy_team_accessibility_settings", // go/trendy/manage/engineers/5641806510587904 trendy_team_id: "5641806510587904", } team { name: "trendy_team_hansmuller_team", // go/trendy/manage/engineers/5069192257765376 trendy_team_id: "5069192257765376", } team { name: "trendy_team_retail_demo_mode", // go/trendy/manage/engineers/6520787531235328 trendy_team_id: "6520787531235328", } team { name: "trendy_team_lse_dreams", // go/trendy/manage/engineers/6317558842097664 trendy_team_id: "6317558842097664", } team { name: "trendy_team_android_usb", // go/trendy/manage/engineers/5090707854426112 trendy_team_id: "5090707854426112", } team { name: "trendy_team_curtisgalloway_team", // go/trendy/manage/engineers/5706857730703360 trendy_team_id: "5706857730703360", } team { name: "trendy_team_camera_algorithms", // go/trendy/manage/engineers/6544854980886528 trendy_team_id: "6544854980886528", } team { name: "trendy_team_cast_3p", // go/trendy/manage/engineers/6585564972875776 trendy_team_id: "6585564972875776", } team { name: "trendy_team_mesch_team", // go/trendy/manage/engineers/5205465899368448 trendy_team_id: "5205465899368448", } team { name: "trendy_team_pixel_system_sw_audio_arch", // go/trendy/manage/engineers/5560501377073152 trendy_team_id: "5560501377073152", } team { name: "trendy_team_defunct_use_controls_quick_settings", // go/trendy/manage/engineers/4667861043412992 trendy_team_id: "4667861043412992", } team { name: "trendy_team_android_rust", // go/trendy/manage/engineers/4844600586305536 trendy_team_id: "4844600586305536", } team { name: "trendy_team_ailabs", // go/trendy/manage/engineers/6673470538285056 trendy_team_id: "6673470538285056", } team { name: "trendy_team_wear_wear_connectivity", // go/trendy/manage/engineers/6245149466263552 trendy_team_id: "6245149466263552", } team { name: "trendy_team_android_core_experiments", // go/trendy/manage/engineers/5709654965780480 trendy_team_id: "5709654965780480", } team { name: "trendy_team_native_tools_libraries", // go/trendy/manage/engineers/5920332376309760 trendy_team_id: "5920332376309760", } team { name: "trendy_team_app_compat", // go/trendy/manage/engineers/4907132411314176 trendy_team_id: "4907132411314176", } team { name: "trendy_team_zra_team", // go/trendy/manage/engineers/6227615267586048 trendy_team_id: "6227615267586048", } team { name: "trendy_team_pixel_watch_system_software", // go/trendy/manage/engineers/5295994500972544 trendy_team_id: "5295994500972544", } team { name: "trendy_team_surfaces_engprod", // go/trendy/manage/engineers/6154478176600064 trendy_team_id: "6154478176600064", } team { name: "trendy_team_android_permissions", // go/trendy/manage/engineers/5533977340313600 trendy_team_id: "5533977340313600", } team { name: "trendy_team_platform_program_mgrs", // go/trendy/manage/engineers/4766394922958848 trendy_team_id: "4766394922958848", } team { name: "trendy_team_deprecated_system_health", // go/trendy/manage/engineers/4864801213644800 trendy_team_id: "4864801213644800", } team { name: "trendy_team_messages", // go/trendy/manage/engineers/5137480097333248 trendy_team_id: "5137480097333248", } team { name: "trendy_team_palmer_team", // go/trendy/manage/engineers/5643570052235264 trendy_team_id: "5643570052235264", } team { name: "trendy_team_android_video_image_codecs", // go/trendy/manage/engineers/5733246110433280 trendy_team_id: "5733246110433280", } team { name: "trendy_team_play_music", // go/trendy/manage/engineers/6015440132112384 trendy_team_id: "6015440132112384", } team { name: "trendy_team_system_clockwork_internal_", // go/trendy/manage/engineers/6509670608797696 trendy_team_id: "6509670608797696", } team { name: "trendy_team_multitasking_windowing", // go/trendy/manage/engineers/5149185436975104 trendy_team_id: "5149185436975104", } team { name: "trendy_team_vr", // go/trendy/manage/engineers/4854355853180928 trendy_team_id: "4854355853180928", } team { name: "trendy_team_maruel_team", // go/trendy/manage/engineers/6302551810146304 trendy_team_id: "6302551810146304", } team { name: "trendy_team_tv_os", // go/trendy/manage/engineers/4662491074134016 trendy_team_id: "4662491074134016", } team { name: "trendy_team_auto_engprod", // go/trendy/manage/engineers/6199949475479552 trendy_team_id: "6199949475479552", } team { name: "trendy_team_sarahcobb_team", // go/trendy/manage/engineers/5755692179947520 trendy_team_id: "5755692179947520", } team { name: "trendy_team_accessibility_services", // go/trendy/manage/engineers/6367283853000704 trendy_team_id: "6367283853000704", } team { name: "trendy_team_documentsui", // go/trendy/manage/engineers/5805983167021056 trendy_team_id: "5805983167021056", } team { name: "trendy_team_carrier_cert_follow_up", // go/trendy/manage/engineers/6751912099741696 trendy_team_id: "6751912099741696", } team { name: "trendy_team_mobile_device_partners", // go/trendy/manage/engineers/5833057717092352 trendy_team_id: "5833057717092352", } team { name: "trendy_team_activity_recognition", // go/trendy/manage/engineers/6304701268000768 trendy_team_id: "6304701268000768", } team { name: "trendy_team_jasoncampbell_team", // go/trendy/manage/engineers/4834972524511232 trendy_team_id: "4834972524511232", } team { name: "trendy_team_glanceables", // go/trendy/manage/engineers/4658222004600832 trendy_team_id: "4658222004600832", } team { name: "trendy_team_android_safe_browsing", // go/trendy/manage/engineers/6685713244782592 trendy_team_id: "6685713244782592", } team { name: "trendy_team_android_input", // go/trendy/manage/engineers/5141994775805952 trendy_team_id: "5141994775805952", } team { name: "trendy_team_android_rust_toolchain", // go/trendy/manage/engineers/6530590989975552 trendy_team_id: "6530590989975552", } team { name: "trendy_team_exo", // go/trendy/manage/engineers/5631545248088064 trendy_team_id: "5631545248088064", } team { name: "trendy_team_android_camera_innovation_team", // go/trendy/manage/engineers/5272590669479936 trendy_team_id: "5272590669479936", } team { name: "trendy_team_accessibility_sound_amplifier", // go/trendy/manage/engineers/5674840312020992 trendy_team_id: "5674840312020992", } team { name: "trendy_team_android_printing", // go/trendy/manage/engineers/6257528146067456 trendy_team_id: "6257528146067456", } team { name: "trendy_team_dtiselice_team", // go/trendy/manage/engineers/5177934253031424 trendy_team_id: "5177934253031424", } team { name: "trendy_team_personal_safety", // go/trendy/manage/engineers/6222285147111424 trendy_team_id: "6222285147111424", } team { name: "trendy_team_notifications", // go/trendy/manage/engineers/5993521355587584 trendy_team_id: "5993521355587584", } team { name: "trendy_team_java_core_libraries", // go/trendy/manage/engineers/4768044190400512 trendy_team_id: "4768044190400512", } team { name: "trendy_team_updatable_sdk_apis", // go/trendy/manage/engineers/4840215139483648 trendy_team_id: "4840215139483648", } team { name: "trendy_team_wear_low_power_mcu_experiences", // go/trendy/manage/engineers/6172878013628416 trendy_team_id: "6172878013628416", } team { name: "trendy_team_biometrics_framework", // go/trendy/manage/engineers/6205415425998848 trendy_team_id: "6205415425998848", } team { name: "trendy_team_pesto", // go/trendy/manage/engineers/5551098528825344 trendy_team_id: "5551098528825344", } team { name: "trendy_team_wear_engineering_foundations", // go/trendy/manage/engineers/5366936275681280 trendy_team_id: "5366936275681280", } team { name: "trendy_team_wear_wcs_developer", // go/trendy/manage/engineers/5114199579459584 trendy_team_id: "5114199579459584", } team { name: "trendy_team_aaos_framework", // go/trendy/manage/engineers/6547794223333376 trendy_team_id: "6547794223333376", } team { name: "trendy_team_clockwork", // go/trendy/manage/engineers/4908781678755840 trendy_team_id: "4908781678755840", } team { name: "trendy_team_pixel_connectivity_wifi_drivers_firmware", // go/trendy/manage/engineers/4583326236934144 trendy_team_id: "4583326236934144", } team { name: "trendy_team_games", // go/trendy/manage/engineers/6736719759933440 trendy_team_id: "6736719759933440", } team { name: "trendy_team_systems_n4_", // go/trendy/manage/engineers/6474486236708864 trendy_team_id: "6474486236708864", } team { name: "trendy_team_android_kvm", // go/trendy/manage/engineers/6529318184714240 trendy_team_id: "6529318184714240", } team { name: "trendy_team_wsd_w22", // go/trendy/manage/engineers/6580039352975360 trendy_team_id: "6580039352975360", } team { name: "trendy_team_android_sudo", // go/trendy/manage/engineers/5329344876380160 trendy_team_id: "5329344876380160", } team { name: "trendy_team_wear_wear_system_health_and_power", // go/trendy/manage/engineers/5219147457658880 trendy_team_id: "5219147457658880", } team { name: "trendy_team_android_build_release_tools", // go/trendy/manage/engineers/6260558107803648 trendy_team_id: "6260558107803648", } team { name: "trendy_team_fused_presence_provider", // go/trendy/manage/engineers/6536344753307648 trendy_team_id: "6536344753307648", } team { name: "trendy_team_agsa", // go/trendy/manage/engineers/6157826887909376 trendy_team_id: "6157826887909376", } team { name: "trendy_team_wear_wear_developer_tiles", // go/trendy/manage/engineers/6633777396613120 trendy_team_id: "6633777396613120", } team { name: "trendy_team_essential_applications", // go/trendy/manage/engineers/4926373864800256 trendy_team_id: "4926373864800256", } team { name: "trendy_team_pixel_mobile_data", // go/trendy/manage/engineers/4996742608977920 trendy_team_id: "4996742608977920", } team { name: "trendy_team_wsd_w52", // go/trendy/manage/engineers/6280972190220288 trendy_team_id: "6280972190220288", } team { name: "trendy_team_pixel_mobile_connectivity", // go/trendy/manage/engineers/6754311945977856 trendy_team_id: "6754311945977856", } team { name: "trendy_team_essentialapps_clock_calculator", // go/trendy/manage/engineers/5270363728674816 trendy_team_id: "5270363728674816", } team { name: "trendy_team_ssd_system_health", // go/trendy/manage/engineers/6456894050664448 trendy_team_id: "6456894050664448", } team { name: "trendy_team_pixel_continuity", // go/trendy/manage/engineers/4786635551309824 trendy_team_id: "4786635551309824", } team { name: "trendy_team_machine_learning", // go/trendy/manage/engineers/5276568318246912 trendy_team_id: "5276568318246912", } team { name: "trendy_team_pixel_ml", // go/trendy/manage/engineers/5339883108990976 trendy_team_id: "5339883108990976", } team { name: "trendy_team_ex_enterprise", // go/trendy/manage/engineers/6738369027375104 trendy_team_id: "6738369027375104", } team { name: "trendy_team_pixel_system_sw_aoc", // go/trendy/manage/engineers/4712464983425024 trendy_team_id: "4712464983425024", } team { name: "trendy_team_android_platform_communications", // go/trendy/manage/engineers/6577505415102464 trendy_team_id: "6577505415102464", } team { name: "trendy_team_sunshine", // go/trendy/manage/engineers/6105050329776128 trendy_team_id: "6105050329776128", } team { name: "trendy_team_qmc_iqt_tao", // go/trendy/manage/engineers/5065462085713920 trendy_team_id: "5065462085713920", } team { name: "trendy_team_mckillop_team", // go/trendy/manage/engineers/5926589599744000 trendy_team_id: "5926589599744000", } team { name: "trendy_team_pixel_process_experience", // go/trendy/manage/engineers/5745436633235456 trendy_team_id: "5745436633235456", } team { name: "trendy_team_wsd_l1", // go/trendy/manage/engineers/5119887911288832 trendy_team_id: "5119887911288832", } team { name: "trendy_team_foldables", // go/trendy/manage/engineers/5149421392920576 trendy_team_id: "5149421392920576", } team { name: "trendy_team_arc_next", // go/trendy/manage/engineers/6238917659361280 trendy_team_id: "6238917659361280", } team { name: "trendy_team_android_rubidium", // go/trendy/manage/engineers/5098012529295360 trendy_team_id: "5098012529295360", } team { name: "trendy_team_deprecated_framework_svetoslavganov", // go/trendy/manage/engineers/6404117492531200 trendy_team_id: "6404117492531200", } team { name: "trendy_team_gregsimon_team", // go/trendy/manage/engineers/5702018510520320 trendy_team_id: "5702018510520320", } team { name: "trendy_team_text_to_speech", // go/trendy/manage/engineers/6368933120442368 trendy_team_id: "6368933120442368", } team { name: "trendy_team_pixel_system_sw_audio", // go/trendy/manage/engineers/6492078422753280 trendy_team_id: "6492078422753280", } team { name: "trendy_team_transformer", // go/trendy/manage/engineers/5964312841420800 trendy_team_id: "5964312841420800", } team { name: "trendy_team_pixel_system_sw_video", // go/trendy/manage/engineers/6442361728696320 trendy_team_id: "6442361728696320", } team { name: "trendy_team_lse_app_compat", // go/trendy/manage/engineers/5180827749154816 trendy_team_id: "5180827749154816", } team { name: "trendy_team_android_media_leads", // go/trendy/manage/engineers/5487674550779904 trendy_team_id: "5487674550779904", } team { name: "trendy_team_kousha_team", // go/trendy/manage/engineers/5157338676887552 trendy_team_id: "5157338676887552", } team { name: "trendy_team_security", // go/trendy/manage/engineers/5241383946158080 trendy_team_id: "5241383946158080", } team { name: "trendy_team_pixel_system_sw_battery_life_system_power_", // go/trendy/manage/engineers/4512957492756480 trendy_team_id: "4512957492756480", } team { name: "trendy_team_eggs", // go/trendy/manage/engineers/4568929198309376 trendy_team_id: "4568929198309376", } team { name: "trendy_team_jeremymanson_team", // go/trendy/manage/engineers/5095869749297152 trendy_team_id: "5095869749297152", } team { name: "trendy_team_exchange_active_sync_in_gmail", // go/trendy/manage/engineers/5382121434513408 trendy_team_id: "5382121434513408", } team { name: "trendy_team_ios_backup_restore_make_pixel_", // go/trendy/manage/engineers/5752160863420416 trendy_team_id: "5752160863420416", } team { name: "trendy_team_deprecated_location", // go/trendy/manage/engineers/6228195632087040 trendy_team_id: "6228195632087040", } team { name: "trendy_team_input_framework", // go/trendy/manage/engineers/4999436357238784 trendy_team_id: "4999436357238784", } team { name: "trendy_team_tpm_tvc", // go/trendy/manage/engineers/5390683333230592 trendy_team_id: "5390683333230592", } team { name: "trendy_team_lse_desktop_os_experience", // go/trendy/manage/engineers/5125234900434944 trendy_team_id: "5125234900434944", } team { name: "trendy_team_android_for_india_device_experiences", // go/trendy/manage/engineers/5395413652111360 trendy_team_id: "5395413652111360", } team { name: "trendy_team_pixel_zombie", // go/trendy/manage/engineers/5074646910533632 trendy_team_id: "5074646910533632", } team { name: "trendy_team_android_onboarding", // go/trendy/manage/engineers/5152271974367232 trendy_team_id: "5152271974367232", } team { name: "trendy_team_pixel_audio", // go/trendy/manage/engineers/5436547260088320 trendy_team_id: "5436547260088320", } team { name: "trendy_team_pixel_connectivity_bt", // go/trendy/manage/engineers/6328035423453184 trendy_team_id: "6328035423453184", } team { name: "trendy_team_wsd_w12", // go/trendy/manage/engineers/6333748748353536 trendy_team_id: "6333748748353536", } team { name: "trendy_team_qmc_mvt", // go/trendy/manage/engineers/4572880876470272 trendy_team_id: "4572880876470272", } team { name: "trendy_team_switch_access_voice_access", // go/trendy/manage/engineers/4794432469467136 trendy_team_id: "4794432469467136", } team { name: "trendy_team_mainline_modularization", // go/trendy/manage/engineers/5845084143386624 trendy_team_id: "5845084143386624", } team { name: "trendy_team_fwk_telecom", // go/trendy/manage/engineers/5330994143821824 trendy_team_id: "5330994143821824", } team { name: "trendy_team_deprecated_framework_akulian", // go/trendy/manage/engineers/5323210872750080 trendy_team_id: "5323210872750080", } team { name: "trendy_team_wear_wear_identity", // go/trendy/manage/engineers/6017732386390016 trendy_team_id: "6017732386390016", } team { name: "trendy_team_android_pdf", // go/trendy/manage/engineers/5175136433045504 trendy_team_id: "5175136433045504", } team { name: "trendy_team_developer_relations", // go/trendy/manage/engineers/5709226143776768 trendy_team_id: "5709226143776768", } team { name: "trendy_team_system_intelligence", // go/trendy/manage/engineers/5849675995709440 trendy_team_id: "5849675995709440", } team { name: "trendy_team_mainline_updates", // go/trendy/manage/engineers/4845810809995264 trendy_team_id: "4845810809995264", } team { name: "trendy_team_n_a_1", // go/trendy/manage/engineers/5946720655376384 trendy_team_id: "5946720655376384", } team { name: "trendy_team_google_drive_docs_sheets_and_slides", // go/trendy/manage/engineers/6613574457622528 trendy_team_id: "6613574457622528", } team { name: "trendy_team_deprecated_awareness_health_experiences", // go/trendy/manage/engineers/6627866395967488 trendy_team_id: "6627866395967488", } team { name: "trendy_team_context_infrastructure", // go/trendy/manage/engineers/4701268040646656 trendy_team_id: "4701268040646656", } team { name: "trendy_team_android_media_solutions", // go/trendy/manage/engineers/4750452004356096 trendy_team_id: "4750452004356096", } team { name: "trendy_team_wear_device_and_infrastructure", // go/trendy/manage/engineers/6358069369798656 trendy_team_id: "6358069369798656", } team { name: "trendy_team_pixel_biometrics", // go/trendy/manage/engineers/5780875748737024 trendy_team_id: "5780875748737024", } team { name: "trendy_team_app_knowledge_platform", // go/trendy/manage/engineers/6272266390634496 trendy_team_id: "6272266390634496", } team { name: "trendy_team_wsd", // go/trendy/manage/engineers/4680083260178432 trendy_team_id: "4680083260178432", } team { name: "trendy_team_seg", // go/trendy/manage/engineers/5067111353155584 trendy_team_id: "5067111353155584", } team { name: "trendy_team_devinlawson_team", // go/trendy/manage/engineers/4805900971442176 trendy_team_id: "4805900971442176", } team { name: "trendy_team_camera_hardware", // go/trendy/manage/engineers/6087458143731712 trendy_team_id: "6087458143731712", } team { name: "trendy_team_camera_image_quality", // go/trendy/manage/engineers/5401362887999488 trendy_team_id: "5401362887999488", } team { name: "trendy_team_android_power_and_comms_infra", // go/trendy/manage/engineers/5325547653332992 trendy_team_id: "5325547653332992", } team { name: "trendy_team_pmw_pmo", // go/trendy/manage/engineers/4656299270504448 trendy_team_id: "4656299270504448", } team { name: "trendy_team_filament", // go/trendy/manage/engineers/6031425915486208 trendy_team_id: "6031425915486208", } team { name: "trendy_team_pixel_system_sw_bspcore", // go/trendy/manage/engineers/6508021341356032 trendy_team_id: "6508021341356032", } team { name: "trendy_team_powermanager_framework", // go/trendy/manage/engineers/5116162121564160 trendy_team_id: "5116162121564160", } team { name: "trendy_team_wear_romanesco", // go/trendy/manage/engineers/5112520062697472 trendy_team_id: "5112520062697472", } team { name: "trendy_team_deprecated_theming", // go/trendy/manage/engineers/5179308179881984 trendy_team_id: "5179308179881984", } team { name: "trendy_team_recorder", // go/trendy/manage/engineers/5085035337383936 trendy_team_id: "5085035337383936", } team { name: "trendy_team_framework_accessibility", // go/trendy/manage/engineers/5474751170019328 trendy_team_id: "5474751170019328", } team { name: "trendy_team_windowing_infra_", // go/trendy/manage/engineers/4578440609431552 trendy_team_id: "4578440609431552", } team { name: "trendy_team_pmw_mcs", // go/trendy/manage/engineers/5864733550608384 trendy_team_id: "5864733550608384", } team { name: "trendy_team_wear_wear_sysui_ctrl_carousel_tiles_recents_launcher_", // go/trendy/manage/engineers/4820131976740864 trendy_team_id: "4820131976740864", } team { name: "trendy_team_wear_wear_accessibility_compliance", // go/trendy/manage/engineers/5381719553114112 trendy_team_id: "5381719553114112", } team { name: "trendy_team_pixel_system_sw_performance_thermal", // go/trendy/manage/engineers/5146276190355456 trendy_team_id: "5146276190355456", } team { name: "trendy_team_neelsa_team", // go/trendy/manage/engineers/5736750978334720 trendy_team_id: "5736750978334720", } team { name: "trendy_team_pixel_camera_engineering_experience", // go/trendy/manage/engineers/5190256655466496 trendy_team_id: "5190256655466496", } team { name: "trendy_team_embedded_web_on_android", // go/trendy/manage/engineers/630061306576896 trendy_team_id: "630061306576896", } team { name: "trendy_team_gantry", // go/trendy/manage/engineers/5677019153858560 trendy_team_id: "5677019153858560", } team { name: "trendy_team_pixel_system_sw_battery", // go/trendy/manage/engineers/6052273771642880 trendy_team_id: "6052273771642880", } team { name: "trendy_team_enigma", // go/trendy/manage/engineers/5396338361597952 trendy_team_id: "5396338361597952", } team { name: "trendy_team_pixel_gps_power", // go/trendy/manage/engineers/5075907446177792 trendy_team_id: "5075907446177792", } team { name: "trendy_team_tool_frank", // go/trendy/manage/engineers/6200209976360960 trendy_team_id: "6200209976360960", } team { name: "trendy_team_rginda_team", // go/trendy/manage/engineers/6031367105314816 trendy_team_id: "6031367105314816", } team { name: "trendy_team_pixel_system_sw_soc_power", // go/trendy/manage/engineers/5400771358785536 trendy_team_id: "5400771358785536", } team { name: "trendy_team_android_crumpet", // go/trendy/manage/engineers/5199704478351360 trendy_team_id: "5199704478351360", } team { name: "trendy_team_wallpapers", // go/trendy/manage/engineers/5125411306373120 trendy_team_id: "5125411306373120", } team { name: "trendy_team_deprecated_volta", // go/trendy/manage/engineers/6316156562309120 trendy_team_id: "6316156562309120", } team { name: "trendy_team_camera_machine_intelligence", // go/trendy/manage/engineers/6578390085533696 trendy_team_id: "6578390085533696", } team { name: "trendy_team_sheepo_team", // go/trendy/manage/engineers/5068061372743680 trendy_team_id: "5068061372743680", } team { name: "trendy_team_android_profile_experiences", // go/trendy/manage/engineers/5914919462404096 trendy_team_id: "5914919462404096", } team { name: "trendy_team_review_platform", // go/trendy/manage/engineers/5952905574514688 trendy_team_id: "5952905574514688", } team { name: "trendy_team_abarth_team", // go/trendy/manage/engineers/4857528786780160 trendy_team_id: "4857528786780160", } team { name: "trendy_team_treble", // go/trendy/manage/engineers/5452490178691072 trendy_team_id: "5452490178691072", } team { name: "trendy_team_jsasinowski_team", // go/trendy/manage/engineers/6239259762786304 trendy_team_id: "6239259762786304", } team { name: "trendy_team_vaas_team", // go/trendy/manage/engineers/5106754296905728 trendy_team_id: "5106754296905728", } team { name: "trendy_team_internationalization", // go/trendy/manage/engineers/5911536283287552 trendy_team_id: "5911536283287552", } team { name: "trendy_team_android_safer_apps", // go/trendy/manage/engineers/5943179005034496 trendy_team_id: "5943179005034496", } team { name: "trendy_team_connectivity_telemetry", // go/trendy/manage/engineers/5084491349393408 trendy_team_id: "5084491349393408", } team { name: "trendy_team_eseidel_team", // go/trendy/manage/engineers/5453997738721280 trendy_team_id: "5453997738721280", } team { name: "trendy_team_test_eng_infrastructure", // go/trendy/manage/engineers/5981905027465216 trendy_team_id: "5981905027465216", } team { name: "trendy_team_wear_wear_wcs_notification", // go/trendy/manage/engineers/4805871527690240 trendy_team_id: "4805871527690240", } team { name: "trendy_team_konkers_team", // go/trendy/manage/engineers/5751147701895168 trendy_team_id: "5751147701895168", } team { name: "trendy_team_mkearney_team", // go/trendy/manage/engineers/5082590844452864 trendy_team_id: "5082590844452864", } team { name: "trendy_team_android_kernel", // go/trendy/manage/engineers/5014334795022336 trendy_team_id: "5014334795022336", } team { name: "trendy_team_chrome", // go/trendy/manage/engineers/6439301864620032 trendy_team_id: "6439301864620032", } team { name: "trendy_team_wear_wear_dialer_messages", // go/trendy/manage/engineers/4906732878725120 trendy_team_id: "4906732878725120", } team { name: "trendy_team_android_tv_engprod", // go/trendy/manage/engineers/5538081157185536 trendy_team_id: "5538081157185536", } team { name: "trendy_team_nicoh_team", // go/trendy/manage/engineers/5662292009189376 trendy_team_id: "5662292009189376", } team { name: "trendy_team_wear_apps_ecosystem", // go/trendy/manage/engineers/4908413871882240 trendy_team_id: "4908413871882240", } team { name: "trendy_team_cellular_security", // go/trendy/manage/engineers/6529004011683840 trendy_team_id: "6529004011683840", } team { name: "trendy_team_dhaloni_team", // go/trendy/manage/engineers/6213556497448960 trendy_team_id: "6213556497448960", } team { name: "trendy_team_applications_google_wide_", // go/trendy/manage/engineers/6120993248378880 trendy_team_id: "6120993248378880", } team { name: "trendy_team_defunct_system_ui_intelligence_dfeng", // go/trendy/manage/engineers/5577284748443648 trendy_team_id: "5577284748443648", } team { name: "trendy_team_oslo", // go/trendy/manage/engineers/5779594887954432 trendy_team_id: "5779594887954432", } team { name: "trendy_team_bluetooth", // go/trendy/manage/engineers/6226546364645376 trendy_team_id: "6226546364645376", } team { name: "trendy_team_localization", // go/trendy/manage/engineers/5751557341446144 trendy_team_id: "5751557341446144", } team { name: "trendy_team_ssd_security", // go/trendy/manage/engineers/4608065248559104 trendy_team_id: "4608065248559104", } team { name: "trendy_team_pixel_system_sw_usb", // go/trendy/manage/engineers/5100646457802752 trendy_team_id: "5100646457802752", } team { name: "trendy_team_hiroshi_team", // go/trendy/manage/engineers/5756564662288384 trendy_team_id: "5756564662288384", } team { name: "trendy_team_contacts", // go/trendy/manage/engineers/4732859818311680 trendy_team_id: "4732859818311680", } team { name: "trendy_team_wsd_w2", // go/trendy/manage/engineers/5945071387934720 trendy_team_id: "5945071387934720", } team { name: "trendy_team_wsd_w51", // go/trendy/manage/engineers/5698780783312896 trendy_team_id: "5698780783312896", } team { name: "trendy_team_erahm_team", // go/trendy/manage/engineers/5666347807309824 trendy_team_id: "5666347807309824", } team { name: "trendy_team_wsd_w53", // go/trendy/manage/engineers/5841167539109888 trendy_team_id: "5841167539109888", } team { name: "trendy_team_framework_overground", // go/trendy/manage/engineers/5135830829891584 trendy_team_id: "5135830829891584", } team { name: "trendy_team_android_performance_console", // go/trendy/manage/engineers/5761662355963904 trendy_team_id: "5761662355963904", } team { name: "trendy_team_partner_modem", // go/trendy/manage/engineers/6502710329376768 trendy_team_id: "6502710329376768", } team { name: "trendy_team_scd_tool", // go/trendy/manage/engineers/5225441027555328 trendy_team_id: "5225441027555328", } team { name: "trendy_team_gtw_sw", // go/trendy/manage/engineers/6069865957687296 trendy_team_id: "6069865957687296", } team { name: "trendy_team_wearables", // go/trendy/manage/engineers/6122642515820544 trendy_team_id: "6122642515820544", } team { name: "trendy_team_android_text", // go/trendy/manage/engineers/5194085585289216 trendy_team_id: "5194085585289216", } team { name: "trendy_team_android_health", // go/trendy/manage/engineers/5177772706004992 trendy_team_id: "5177772706004992", } team { name: "trendy_team_wsd_w23", // go/trendy/manage/engineers/6191361992556544 trendy_team_id: "6191361992556544", } team { name: "trendy_team_pixel_connectivity_networking", // go/trendy/manage/engineers/6685592469241856 trendy_team_id: "6685592469241856", } team { name: "trendy_team_ppi_team", // go/trendy/manage/engineers/5171933646848000 trendy_team_id: "5171933646848000", } team { name: "trendy_team_ssd_mm_peripheral", // go/trendy/manage/engineers/6624019818086400 trendy_team_id: "6624019818086400", } team { name: "trendy_team_n_a", // go/trendy/manage/engineers/4891189492711424 trendy_team_id: "4891189492711424", } team { name: "trendy_team_pixel_system_sw_hid_driver", // go/trendy/manage/engineers/4534130425102336 trendy_team_id: "4534130425102336", } team { name: "trendy_team_wear_wearable_motion_algorithms", // go/trendy/manage/engineers/5397550198587392 trendy_team_id: "5397550198587392", } team { name: "trendy_team_wear_wear_sysui_notifications", // go/trendy/manage/engineers/5256257183055872 trendy_team_id: "5256257183055872", } team { name: "trendy_team_android_camera_engprod", // go/trendy/manage/engineers/5594382843281408 trendy_team_id: "5594382843281408", } team { name: "trendy_team_lockscreen_aod", // go/trendy/manage/engineers/5503979641012224 trendy_team_id: "5503979641012224", } team { name: "trendy_team_windowing_sdk", // go/trendy/manage/engineers/5683037008723968 trendy_team_id: "5683037008723968", } team { name: "trendy_team_pixel_system_sw_inmarket_power", // go/trendy/manage/engineers/6675891331170304 trendy_team_id: "6675891331170304", } team { name: "trendy_team_betterbug", // go/trendy/manage/engineers/4910400652607488 trendy_team_id: "4910400652607488", } team { name: "trendy_team_pixel_system_sw_security", // go/trendy/manage/engineers/5030277713625088 trendy_team_id: "5030277713625088", } team { name: "trendy_team_pixel_energizer", // go/trendy/manage/engineers/4970605270302720 trendy_team_id: "4970605270302720", } team { name: "trendy_team_fwk_core_networking", // go/trendy/manage/engineers/5559692562399232 trendy_team_id: "5559692562399232", } team { name: "trendy_team_chromium_webview", // go/trendy/manage/engineers/5630061306576896 trendy_team_id: "5630061306576896", } team { name: "trendy_team_framework_cdm", // go/trendy/manage/engineers/4793721887031296 trendy_team_id: "4793721887031296", } team { name: "trendy_team_system_walleye_", // go/trendy/manage/engineers/5665245678665728 trendy_team_id: "5665245678665728", } team { name: "trendy_team_system_marlin_sailfish_", // go/trendy/manage/engineers/4713618364825600 trendy_team_id: "4713618364825600", } team { name: "trendy_team_qmc", // go/trendy/manage/engineers/5207848841510912 trendy_team_id: "5207848841510912", } team { name: "trendy_team_android_wallet_integration", // go/trendy/manage/engineers/5785777995153408 trendy_team_id: "5785777995153408", } team { name: "trendy_team_noreent_team", // go/trendy/manage/engineers/5766299843198976 trendy_team_id: "5766299843198976", } team { name: "trendy_team_ink", // go/trendy/manage/engineers/6620225162608640 trendy_team_id: "6620225162608640", } team { name: "trendy_team_make_pixel", // go/trendy/manage/engineers/6140234701864960 trendy_team_id: "6140234701864960", } team { name: "trendy_team_chillers_team", // go/trendy/manage/engineers/5631647887294464 trendy_team_id: "5631647887294464", } team { name: "trendy_team_system_experience", // go/trendy/manage/engineers/5083633521950720 trendy_team_id: "5083633521950720", } team { name: "trendy_team_deprecated_framework_roosa", // go/trendy/manage/engineers/6708067074998272 trendy_team_id: "6708067074998272", } team { name: "trendy_team_build", // go/trendy/manage/engineers/5542100376354816 trendy_team_id: "5542100376354816", } team { name: "trendy_team_play_store", // go/trendy/manage/engineers/4803228562489344 trendy_team_id: "4803228562489344", } team { name: "trendy_team_clocks", // go/trendy/manage/engineers/6327058391007232 trendy_team_id: "6327058391007232", } team { name: "trendy_team_asafi_team", // go/trendy/manage/engineers/6217735399964672 trendy_team_id: "6217735399964672", } team { name: "trendy_team_pixel_system_sw_storage", // go/trendy/manage/engineers/4644898888089600 trendy_team_id: "4644898888089600", } team { name: "trendy_team_play_movies", // go/trendy/manage/engineers/4838412934578176 trendy_team_id: "4838412934578176", } team { name: "trendy_team_system_hammerhead_camera_", // go/trendy/manage/engineers/6597631539019776 trendy_team_id: "6597631539019776", } team { name: "trendy_team_wear_wear_sysui_applications", // go/trendy/manage/engineers/4929833494544384 trendy_team_id: "4929833494544384", } team { name: "trendy_team_pixel_system_sw_tpm", // go/trendy/manage/engineers/4612922981122048 trendy_team_id: "4612922981122048", } team { name: "trendy_team_qmc_script_automation", // go/trendy/manage/engineers/5047869899669504 trendy_team_id: "5047869899669504", } team { name: "trendy_team_pixel_sw_tpm", // go/trendy/manage/engineers/5506916004265984 trendy_team_id: "5506916004265984", } team { name: "trendy_team_device_and_factory_tpm", // go/trendy/manage/engineers/4574530143911936 trendy_team_id: "4574530143911936", } team { name: "trendy_team_pmw_mss", // go/trendy/manage/engineers/4525262032896000 trendy_team_id: "4525262032896000", } team { name: "trendy_team_wear_wear_esim_and_carriers", // go/trendy/manage/engineers/5045168113614848 trendy_team_id: "5045168113614848", } team { name: "trendy_team_android_pixel_context_hub", // go/trendy/manage/engineers/5375970200944640 trendy_team_id: "5375970200944640", } team { name: "trendy_team_pixel_setting_exp", // go/trendy/manage/engineers/5758010936295424 trendy_team_id: "5758010936295424", } team { name: "trendy_team_defunct_system_ui_intelligence_praveenj", // go/trendy/manage/engineers/6648758829711360 trendy_team_id: "6648758829711360", } team { name: "trendy_team_system_bullhead_", // go/trendy/manage/engineers/4592122329956352 trendy_team_id: "4592122329956352", } team { name: "trendy_team_pixel_system_sw_fingerprint", // go/trendy/manage/engineers/5380181285568512 trendy_team_id: "5380181285568512", } team { name: "trendy_team_android_sdlc", // go/trendy/manage/engineers/6492896504152064 trendy_team_id: "6492896504152064", } team { name: "trendy_team_android_core_graphics_stack", // go/trendy/manage/engineers/5260625399644160 trendy_team_id: "5260625399644160", } team { name: "trendy_team_accessibility_switch_access", // go/trendy/manage/engineers/6026869039857664 trendy_team_id: "6026869039857664", } team { name: "trendy_team_sqa_make_pixel_", // go/trendy/manage/engineers/5610819853090816 trendy_team_id: "5610819853090816", } team { name: "trendy_team_controls", // go/trendy/manage/engineers/5005994102259712 trendy_team_id: "5005994102259712", } team { name: "trendy_team_renderscript_nnapi", // go/trendy/manage/engineers/6527262794842112 trendy_team_id: "6527262794842112", } team { name: "trendy_team_test_infrastructure", // go/trendy/manage/engineers/5130189115654144 trendy_team_id: "5130189115654144", } team { name: "trendy_team_ssd_peripheral", // go/trendy/manage/engineers/6314507294867456 trendy_team_id: "6314507294867456", } team { name: "trendy_team_device_connectivity_experiences_make_pixel_", // go/trendy/manage/engineers/5348586329866240 trendy_team_id: "5348586329866240", } team { name: "trendy_team_nandunair_team", // go/trendy/manage/engineers/4874500384129024 trendy_team_id: "4874500384129024", } team { name: "trendy_team_godofredoc_team", // go/trendy/manage/engineers/4892528710156288 trendy_team_id: "4892528710156288", } team { name: "trendy_team_gtw_misc", // go/trendy/manage/engineers/6437652597178368 trendy_team_id: "6437652597178368", } team { name: "trendy_team_perception_virtualization", // go/trendy/manage/engineers/5133931925897216 trendy_team_id: "5133931925897216", } team { name: "trendy_team_safety_els_earthquake", // go/trendy/manage/engineers/6508498165071872 trendy_team_id: "6508498165071872", } team { name: "trendy_team_virtual_device_framework", // go/trendy/manage/engineers/4798040542445568 trendy_team_id: "4798040542445568", } team { name: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_", // go/trendy/manage/engineers/6402468225089536 trendy_team_id: "6402468225089536", } team { name: "trendy_team_android_media_reliability", // go/trendy/manage/engineers/5489323818221568 trendy_team_id: "5489323818221568", } team { name: "trendy_team_wear_wear_services", // go/trendy/manage/engineers/5140566757179392 trendy_team_id: "5140566757179392", } team { name: "trendy_team_qmc_pda", // go/trendy/manage/engineers/5155072283377664 trendy_team_id: "5155072283377664", } team { name: "trendy_team_vsl", // go/trendy/manage/engineers/6562447166930944 trendy_team_id: "6562447166930944", } team { name: "trendy_team_android_release_metrics", // go/trendy/manage/engineers/6018925759201280 trendy_team_id: "6018925759201280", } team { name: "trendy_team_testing", // go/trendy/manage/engineers/5892294829801472 trendy_team_id: "5892294829801472", } team { name: "trendy_team_deprecated_wallet_integration", // go/trendy/manage/engineers/5286726042288128 trendy_team_id: "5286726042288128", } team { name: "trendy_team_leonardchan_team", // go/trendy/manage/engineers/6260994579005440 trendy_team_id: "6260994579005440", } team { name: "trendy_team_system_performance", // go/trendy/manage/engineers/5188607388024832 trendy_team_id: "5188607388024832", } team { name: "trendy_team_system_power", // go/trendy/manage/engineers/4820820748533760 trendy_team_id: "4820820748533760", } team { name: "trendy_team_deprecated_gdm_location_ads_marketplaces", // go/trendy/manage/engineers/5261636812570624 trendy_team_id: "5261636812570624", } team { name: "trendy_team_pixel_system_sw_digital_key", // go/trendy/manage/engineers/6444896766033920 trendy_team_id: "6444896766033920", } team { name: "trendy_team_aosp", // go/trendy/manage/engineers/4860855378018304 trendy_team_id: "4860855378018304", } team { name: "trendy_team_launcher", // go/trendy/manage/engineers/5102295725244416 trendy_team_id: "5102295725244416", } team { name: "trendy_team_ime", // go/trendy/manage/engineers/6085808876290048 trendy_team_id: "6085808876290048", } team { name: "trendy_team_jyotiraju_team", // go/trendy/manage/engineers/5720977167253504 trendy_team_id: "5720977167253504", } team { name: "trendy_team_camera", // go/trendy/manage/engineers/5718022236798976 trendy_team_id: "5718022236798976", } team { name: "trendy_team_wear_wear_backup_restore", // go/trendy/manage/engineers/4875982171176960 trendy_team_id: "4875982171176960", } team { name: "trendy_team_wear_wear_developer_watch_faces_complications", // go/trendy/manage/engineers/5638213037096960 trendy_team_id: "5638213037096960", } team { name: "trendy_team_mainline_reach", // go/trendy/manage/engineers/5701386012098560 trendy_team_id: "5701386012098560", } team { name: "trendy_team_ssd_bsp", // go/trendy/manage/engineers/5876351911198720 trendy_team_id: "5876351911198720", } team { name: "trendy_team_ux_design", // go/trendy/manage/engineers/4678433992736768 trendy_team_id: "4678433992736768", } team { name: "trendy_team_accessibility_fw_make_pixel", // go/trendy/manage/engineers/5522858922868736 trendy_team_id: "5522858922868736", } team { name: "trendy_team_wear_wear_media", // go/trendy/manage/engineers/5365411545513984 trendy_team_id: "5365411545513984", } team { name: "trendy_team_system_angler_", // go/trendy/manage/engineers/5593227667046400 trendy_team_id: "5593227667046400", } team { name: "trendy_team_pixel_system_sw_tools", // go/trendy/manage/engineers/6460475572912128 trendy_team_id: "6460475572912128", } team { name: "trendy_team_platform_build", // go/trendy/manage/engineers/5774403578920960 trendy_team_id: "5774403578920960", } team { name: "trendy_team_pchitoor_team", // go/trendy/manage/engineers/5962577315266560 trendy_team_id: "5962577315266560", } team { name: "trendy_team_multi_device_platform", // go/trendy/manage/engineers/5850153090711552 trendy_team_id: "5850153090711552", } team { name: "trendy_team_safetynet", // go/trendy/manage/engineers/4748802736914432 trendy_team_id: "4748802736914432", } team { name: "trendy_team_android_resources", // go/trendy/manage/engineers/4678767020703744 trendy_team_id: "4678767020703744", } team { name: "trendy_team_joshconner_team", // go/trendy/manage/engineers/6226828248383488 trendy_team_id: "6226828248383488", } team { name: "trendy_team_qmc_ait", // go/trendy/manage/engineers/6175419073953792 trendy_team_id: "6175419073953792", } team { name: "trendy_team_pixel_global", // go/trendy/manage/engineers/4609714516000768 trendy_team_id: "4609714516000768", } team { name: "trendy_team_qa_automation", // go/trendy/manage/engineers/6159878303678464 trendy_team_id: "6159878303678464", } team { name: "trendy_team_android_gpu", // go/trendy/manage/engineers/6105848565104640 trendy_team_id: "6105848565104640", } team { name: "trendy_team_qmc_ate", // go/trendy/manage/engineers/4819171481092096 trendy_team_id: "4819171481092096", } team { name: "trendy_team_hangouts", // go/trendy/manage/engineers/6263380004175872 trendy_team_id: "6263380004175872", } team { name: "trendy_team_cross_device_control", // go/trendy/manage/engineers/5888607197757440 trendy_team_id: "5888607197757440", } team { name: "trendy_team_interactions_frameworks", // go/trendy/manage/engineers/4795124029489152 trendy_team_id: "4795124029489152", } team { name: "trendy_team_deprecated_framework_santoscordon", // go/trendy/manage/engineers/6049242537951232 trendy_team_id: "6049242537951232", } team { name: "trendy_team_authentication", // go/trendy/manage/engineers/5901909380988928 trendy_team_id: "5901909380988928", } team { name: "trendy_team_stylus", // go/trendy/manage/engineers/5685003218747392 trendy_team_id: "5685003218747392", } team { name: "trendy_team_linus_team", // go/trendy/manage/engineers/6210035100844032 trendy_team_id: "6210035100844032", } team { name: "trendy_team_virtualization", // go/trendy/manage/engineers/5117131519787008 trendy_team_id: "5117131519787008", } team { name: "trendy_team_billstevenson_team", // go/trendy/manage/engineers/5631064744820736 trendy_team_id: "5631064744820736", } team { name: "trendy_team_android_smartos", // go/trendy/manage/engineers/5637973325414400 trendy_team_id: "5637973325414400", } team { name: "trendy_team_art_performance", // go/trendy/manage/engineers/6210603446042624 trendy_team_id: "6210603446042624", } team { name: "trendy_team_ssd", // go/trendy/manage/engineers/5858759725154304 trendy_team_id: "5858759725154304", } team { name: "trendy_team_abc_engops", // go/trendy/manage/engineers/5273578928504832 trendy_team_id: "5273578928504832", } team { name: "trendy_team_wear_weather_android_app", // go/trendy/manage/engineers/6496415568003072 trendy_team_id: "6496415568003072", } team { name: "trendy_team_rubidium_sdk_runtime", // go/trendy/manage/engineers/6286508355911680 trendy_team_id: "6286508355911680", } team { name: "trendy_team_borthakur_team", // go/trendy/manage/engineers/4962243059023872 trendy_team_id: "4962243059023872", } team { name: "trendy_team_pixel_system_sw_graphics", // go/trendy/manage/engineers/5791644907110400 trendy_team_id: "5791644907110400", } team { name: "trendy_team_make_creative_android_key_experience", // go/trendy/manage/engineers/5716738515173376 trendy_team_id: "5716738515173376", } team { name: "trendy_team_pmw_l1rf", // go/trendy/manage/engineers/5302906915684352 trendy_team_id: "5302906915684352", } team { name: "trendy_team_test_eng_comms_power", // go/trendy/manage/engineers/6632815911108608 trendy_team_id: "6632815911108608", } team { name: "trendy_team_wear_wear_architecture_group", // go/trendy/manage/engineers/5609928060207104 trendy_team_id: "5609928060207104", } team { name: "trendy_team_wear_wear_esim_carriers", // go/trendy/manage/engineers/5928361498935296 trendy_team_id: "5928361498935296", } team { name: "trendy_team_pixel_connectivity_gps", // go/trendy/manage/engineers/4920660539899904 trendy_team_id: "4920660539899904", } team { name: "trendy_team_adversarial_code_ai_arc_ai_", // go/trendy/manage/engineers/4850213657673728 trendy_team_id: "4850213657673728", } team { name: "trendy_team_android_binary_transparency", // go/trendy/manage/engineers/6585365243002880 trendy_team_id: "6585365243002880", } team { name: "trendy_team_test_eng_automotive_tv", // go/trendy/manage/engineers/6156177620467712 trendy_team_id: "6156177620467712", } team { name: "trendy_team_tgosselaar_team", // go/trendy/manage/engineers/4897638077071360 trendy_team_id: "4897638077071360", } team { name: "trendy_team_pixel_connectivity_wifi", // go/trendy/manage/engineers/4776219313340416 trendy_team_id: "4776219313340416", } team { name: "trendy_team_setup_wizard", // go/trendy/manage/engineers/5417305806602240 trendy_team_id: "5417305806602240", } team { name: "trendy_team_security_validation_engineering_sve_", // go/trendy/manage/engineers/5850943050907648 trendy_team_id: "5850943050907648", } team { name: "trendy_team_wsd_function", // go/trendy/manage/engineers/6650408097153024 trendy_team_id: "6650408097153024", } team { name: "trendy_team_platform_product_mgrs", // go/trendy/manage/engineers/6483282329731072 trendy_team_id: "6483282329731072", } team { name: "trendy_team_partner_telephony", // go/trendy/manage/engineers/5767882120265728 trendy_team_id: "5767882120265728", } team { name: "trendy_team_crjohns_team", // go/trendy/manage/engineers/4804101473992704 trendy_team_id: "4804101473992704", } team { name: "trendy_team_wsd_ims", // go/trendy/manage/engineers/6050624504201216 trendy_team_id: "6050624504201216", } team { name: "trendy_team_wear_wear_weather", // go/trendy/manage/engineers/5464164419928064 trendy_team_id: "5464164419928064", } team { name: "trendy_team_guptaritu_team", // go/trendy/manage/engineers/5142679624777728 trendy_team_id: "5142679624777728", } team { name: "trendy_team_wear_wear_developer_tools", // go/trendy/manage/engineers/6228915878002688 trendy_team_id: "6228915878002688", } team { name: "trendy_team_play_books", // go/trendy/manage/engineers/5769149527490560 trendy_team_id: "5769149527490560", } team { name: "trendy_team_melissadaniels_team", // go/trendy/manage/engineers/5715112926281728 trendy_team_id: "5715112926281728", } team { name: "trendy_team_wear_shared_context_state", // go/trendy/manage/engineers/5329107344588800 trendy_team_id: "5329107344588800", } team { name: "trendy_team_motion", // go/trendy/manage/engineers/6331351269277696 trendy_team_id: "6331351269277696", } team { name: "trendy_team_gpp_on_device", // go/trendy/manage/engineers/5181961504980992 trendy_team_id: "5181961504980992", } team { name: "trendy_team_android_settings_app", // go/trendy/manage/engineers/6204400884154368 trendy_team_id: "6204400884154368", } team { name: "trendy_team_l1_inmarket", // go/trendy/manage/engineers/5172846450737152 trendy_team_id: "5172846450737152", } team { name: "trendy_team_wear_wearflow", // go/trendy/manage/engineers/5947250429558784 trendy_team_id: "5947250429558784", } team { name: "trendy_team_enterprise", // go/trendy/manage/engineers/5366178515910656 trendy_team_id: "5366178515910656", } team { name: "trendy_team_deprecated_framework_michaelwr", // go/trendy/manage/engineers/4574277124915200 trendy_team_id: "4574277124915200", } team { name: "trendy_team_color", // go/trendy/manage/engineers/6305208086724608 trendy_team_id: "6305208086724608", } team { name: "trendy_team_osbornc_team", // go/trendy/manage/engineers/6319504650043392 trendy_team_id: "6319504650043392", } team { name: "trendy_team_dialer", // go/trendy/manage/engineers/6595982271578112 trendy_team_id: "6595982271578112", } team { name: "trendy_team_framework_bpm", // go/trendy/manage/engineers/5498119911243776 trendy_team_id: "5498119911243776", } team { name: "trendy_team_wsd_core_team", // go/trendy/manage/engineers/6683943201800192 trendy_team_id: "6683943201800192", } team { name: "trendy_team_pixel_connectivity_ril", // go/trendy/manage/engineers/4995093341536256 trendy_team_id: "4995093341536256", } team { name: "trendy_team_perfetto", // go/trendy/manage/engineers/4783987109003264 trendy_team_id: "4783987109003264", } team { name: "trendy_team_partner_devrel", // go/trendy/manage/engineers/4537696504381440 trendy_team_id: "4537696504381440", } team { name: "trendy_team_fwk_thread_network", // go/trendy/manage/engineers/5094685775134720 trendy_team_id: "5094685775134720", } team { name: "trendy_team_thatguy_team", // go/trendy/manage/engineers/5688369775542272 trendy_team_id: "5688369775542272", } team { name: "trendy_team_finder", // go/trendy/manage/engineers/6492831025364992 trendy_team_id: "6492831025364992", } team { name: "trendy_team_boot_time", // go/trendy/manage/engineers/6017089399554048 trendy_team_id: "6017089399554048", } team { name: "trendy_team_wear_wcs_beto", // go/trendy/manage/engineers/4910945436336128 trendy_team_id: "4910945436336128", } team { name: "trendy_team_responsible_apis", // go/trendy/manage/engineers/5651962854178816 trendy_team_id: "5651962854178816", } team { name: "trendy_team_pixel_system_sw_corebsp", // go/trendy/manage/engineers/4626673699553280 trendy_team_id: "4626673699553280", } team { name: "trendy_team_education", // go/trendy/manage/engineers/4889540225269760 trendy_team_id: "4889540225269760", } team { name: "trendy_team_alarm_clock", // go/trendy/manage/engineers/6193011259998208 trendy_team_id: "6193011259998208", } team { name: "trendy_team_pmw_tvc", // go/trendy/manage/engineers/4526288764960768 trendy_team_id: "4526288764960768", } team { name: "trendy_team_deprecated_pixel_wifi", // go/trendy/manage/engineers/5741405413900288 trendy_team_id: "5741405413900288", } team { name: "trendy_team_abdulla_team", // go/trendy/manage/engineers/6223585145421824 trendy_team_id: "6223585145421824", } team { name: "trendy_team_hardware", // go/trendy/manage/engineers/5357382422888448 trendy_team_id: "5357382422888448", } team { name: "trendy_team_status_bar", // go/trendy/manage/engineers/6329516043173888 trendy_team_id: "6329516043173888", } team { name: "trendy_team_wear_wear_sysui_big_picture", // go/trendy/manage/engineers/5081356719521792 trendy_team_id: "5081356719521792", } team { name: "trendy_team_wear_material_design_for_wearos", // go/trendy/manage/engineers/4792942333952000 trendy_team_id: "4792942333952000", } team { name: "trendy_team_platform_security", // go/trendy/manage/engineers/5243033213599744 trendy_team_id: "5243033213599744", } team { name: "trendy_team_llvm_and_toolchains", // go/trendy/manage/engineers/5990701120487424 trendy_team_id: "5990701120487424", } team { name: "trendy_team_jmccandless_team", // go/trendy/manage/engineers/5227794226380800 trendy_team_id: "5227794226380800", } team { name: "trendy_team_safety_center", // go/trendy/manage/engineers/5930273843609600 trendy_team_id: "5930273843609600", } team { name: "trendy_team_wear_wear_systems_engineering_and_devices", // go/trendy/manage/engineers/5407847298793472 trendy_team_id: "5407847298793472", } team { name: "trendy_team_framework_android_multiuser", // go/trendy/manage/engineers/5981525732392960 trendy_team_id: "5981525732392960", } team { name: "trendy_team_deprecated_test2", // go/trendy/manage/engineers/4590958690074624 trendy_team_id: "4590958690074624", } team { name: "trendy_team_qmc_iqt_tpe", // go/trendy/manage/engineers/6668000283197440 trendy_team_id: "6668000283197440", } team { name: "trendy_team_pixel_system_sw_factory", // go/trendy/manage/engineers/6267032573739008 trendy_team_id: "6267032573739008", } team { name: "trendy_team_automotive", // go/trendy/manage/engineers/5770798794932224 trendy_team_id: "5770798794932224", } team { name: "trendy_team_aaos_display_safety_triage", // go/trendy/manage/engineers/6522093663780864 trendy_team_id: "6522093663780864", } team { name: "trendy_team_camera_htc_lg_qualcomm", // go/trendy/manage/engineers/6332099480911872 trendy_team_id: "6332099480911872", } team { name: "trendy_team_rkp_keystore", // go/trendy/manage/engineers/5634304374505472 trendy_team_id: "5634304374505472", } team { name: "trendy_team_wear_wear_watch_faces", // go/trendy/manage/engineers/5885708195495936 trendy_team_id: "5885708195495936", } team { name: "trendy_team_arc_app_compat", // go/trendy/manage/engineers/4811894441279488 trendy_team_id: "4811894441279488", } team { name: "trendy_team_psohn_team", // go/trendy/manage/engineers/4852673947009024 trendy_team_id: "4852673947009024", } team { name: "trendy_team_hollande_team", // go/trendy/manage/engineers/5356533186723840 trendy_team_id: "5356533186723840", } team { name: "trendy_team_wear_wear_partner_programs_and_engineering_team", // go/trendy/manage/engineers/4934997571960832 trendy_team_id: "4934997571960832", } team { name: "trendy_team_pixel_connectivity_nfc", // go/trendy/manage/engineers/5631272051965952 trendy_team_id: "5631272051965952", } team { name: "trendy_team_wear_wear_ios_connectivity", // go/trendy/manage/engineers/4702455644192768 trendy_team_id: "4702455644192768", } team { name: "trendy_team_arc_", // go/trendy/manage/engineers/4556937957867520 trendy_team_id: "4556937957867520", } team { name: "trendy_team_android_one", // go/trendy/manage/engineers/6272176097198080 trendy_team_id: "6272176097198080", } team { name: "trendy_team_ccherubino_team", // go/trendy/manage/engineers/4846471192150016 trendy_team_id: "4846471192150016", } team { name: "trendy_team_deprecated_bluetooth_and_nfc", // go/trendy/manage/engineers/5259280851435520 trendy_team_id: "5259280851435520", } team { name: "trendy_team_test_eng_afw_auth_location_camera_media", // go/trendy/manage/engineers/6298564376264704 trendy_team_id: "6298564376264704", } team { name: "trendy_team_frousseau_team", // go/trendy/manage/engineers/5718296436572160 trendy_team_id: "5718296436572160", } team { name: "trendy_team_pixel_system_sw_touch_haptic", // go/trendy/manage/engineers/6469264330096640 trendy_team_id: "6469264330096640", } team { name: "trendy_team_partner_eng", // go/trendy/manage/engineers/5172664469422080 trendy_team_id: "5172664469422080", } team { name: "trendy_team_dogfooders", // go/trendy/manage/engineers/4643249620647936 trendy_team_id: "4643249620647936", } team { name: "trendy_team_system_external_", // go/trendy/manage/engineers/5558043294957568 trendy_team_id: "5558043294957568", } team { name: "trendy_team_foundations", // go/trendy/manage/engineers/6216250952679424 trendy_team_id: "6216250952679424", } team { name: "trendy_team_camera_framework", // go/trendy/manage/engineers/6455244783222784 trendy_team_id: "6455244783222784", } team { name: "trendy_team_bugjuggler", // go/trendy/manage/engineers/6472836969267200 trendy_team_id: "6472836969267200", } team { name: "trendy_team_cligh_team", // go/trendy/manage/engineers/6273778455314432 trendy_team_id: "6273778455314432", } team { name: "trendy_team_android_testing_experiences", // go/trendy/manage/engineers/5653137056366592 trendy_team_id: "5653137056366592", } team { name: "trendy_team_dx", // go/trendy/manage/engineers/5677977168281600 trendy_team_id: "5677977168281600", } team { name: "trendy_team_framework_backstage_power", // go/trendy/manage/engineers/6314066964283392 trendy_team_id: "6314066964283392", } team { name: "trendy_team_rlb_team", // go/trendy/manage/engineers/5206858878058496 trendy_team_id: "5206858878058496", } team { name: "trendy_team_pixel_system_sw_pts", // go/trendy/manage/engineers/5553725663510528 trendy_team_id: "5553725663510528", } team { name: "trendy_team_keir_team", // go/trendy/manage/engineers/5731700089978880 trendy_team_id: "5731700089978880", } team { name: "trendy_team_system_taimen_", // go/trendy/manage/engineers/6421709678575616 trendy_team_id: "6421709678575616", } team { name: "trendy_team_security_response", // go/trendy/manage/engineers/5031926981066752 trendy_team_id: "5031926981066752", } team { name: "trendy_team_preload_safety", // go/trendy/manage/engineers/4584609580744704 trendy_team_id: "4584609580744704", } team { name: "trendy_team_pixel_customizations_make_", // go/trendy/manage/engineers/5155643836366848 trendy_team_id: "5155643836366848", } team { name: "trendy_team_tooltopia", // go/trendy/manage/engineers/5456472883101696 trendy_team_id: "5456472883101696", } team { name: "trendy_team_accessibility_live_transcribe", // go/trendy/manage/engineers/6299695642345472 trendy_team_id: "6299695642345472", } team { name: "trendy_team_trusty", // go/trendy/manage/engineers/5109319549616128 trendy_team_id: "5109319549616128", } team { name: "trendy_team_amathes_team", // go/trendy/manage/engineers/5157715862257664 trendy_team_id: "5157715862257664", } team { name: "trendy_team_wear_wear_developer_android_devrel_", // go/trendy/manage/engineers/5861820594028544 trendy_team_id: "5861820594028544", } team { name: "trendy_team_overview", // go/trendy/manage/engineers/5071790575550464 trendy_team_id: "5071790575550464", } team { name: "trendy_team_android_sensors", // go/trendy/manage/engineers/4776371090259968 trendy_team_id: "4776371090259968", } team { name: "trendy_team_ui_toolkit", // go/trendy/manage/engineers/5638857399599104 trendy_team_id: "5638857399599104", } team { name: "trendy_team_gesture_nav", // go/trendy/manage/engineers/6304405391310848 trendy_team_id: "6304405391310848", } team { name: "trendy_team_qmc_wifi_storage", // go/trendy/manage/engineers/4924724597358592 trendy_team_id: "4924724597358592", } team { name: "trendy_team_wsd_w11", // go/trendy/manage/engineers/5929128469331968 trendy_team_id: "5929128469331968", } team { name: "trendy_team_fsamuel_team", // go/trendy/manage/engineers/5753514497310720 trendy_team_id: "5753514497310720", } team { name: "trendy_team_pixel_haptic", // go/trendy/manage/engineers/5919013003493376 trendy_team_id: "5919013003493376", } team { name: "trendy_team_pixel_retention", // go/trendy/manage/engineers/5647985290805248 trendy_team_id: "5647985290805248", } team { name: "trendy_team_pixel_onboarding", // go/trendy/manage/engineers/5531340811960320 trendy_team_id: "5531340811960320", } team { name: "trendy_team_wsd_standard", // go/trendy/manage/engineers/6296915108823040 trendy_team_id: "6296915108823040", } team { name: "trendy_team_art_mainline", // go/trendy/manage/engineers/5733965155401728 trendy_team_id: "5733965155401728", } team { name: "trendy_team_shade", // go/trendy/manage/engineers/5646715170226176 trendy_team_id: "5646715170226176", } team { name: "trendy_team_gchips_compute_sw", // go/trendy/manage/engineers/6245787818131456 trendy_team_id: "6245787818131456", } team { name: "trendy_team_haptics_framework", // go/trendy/manage/engineers/5895438509441024 trendy_team_id: "5895438509441024", } team { name: "trendy_team_accessibility_braille", // go/trendy/manage/engineers/4992530205933568 trendy_team_id: "4992530205933568", } team { name: "trendy_team_qmc_utd", // go/trendy/manage/engineers/5524508190310400 trendy_team_id: "5524508190310400", } team { name: "trendy_team_android_on", // go/trendy/manage/engineers/4539345771823104 trendy_team_id: "4539345771823104", } team { name: "trendy_team_fit", // go/trendy/manage/engineers/5628412039135232 trendy_team_id: "5628412039135232", } team { name: "trendy_team_fwk_uwb", // go/trendy/manage/engineers/5983733408235520 trendy_team_id: "5983733408235520", } team { name: "trendy_team_aidroid", // go/trendy/manage/engineers/4697675446222848 trendy_team_id: "4697675446222848", } team { name: "trendy_team_deprecated_test1", // go/trendy/manage/engineers/6212752467460096 trendy_team_id: "6212752467460096", } team { name: "trendy_team_wear_wear_releases_telemetry_and_analytics", // go/trendy/manage/engineers/5892057298010112 trendy_team_id: "5892057298010112", } team { name: "trendy_team_context_hub", // go/trendy/manage/engineers/5080704467501056 trendy_team_id: "5080704467501056", } team { name: "trendy_team_carrier_follow_up", // go/trendy/manage/engineers/6615223725064192 trendy_team_id: "6615223725064192", } team { name: "trendy_team_accessibility_live_caption", // go/trendy/manage/engineers/4764529665409024 trendy_team_id: "4764529665409024", } team { name: "trendy_team_pixel_system_sw_display", // go/trendy/manage/engineers/6261730736734208 trendy_team_id: "6261730736734208", } team { name: "trendy_team_foundation_security_rust_pkvm_", // go/trendy/manage/engineers/5071354421084160 trendy_team_id: "5071354421084160", } team { name: "trendy_team_pixel_repair_mode", // go/trendy/manage/engineers/6083775867813888 trendy_team_id: "6083775867813888", } team { name: "trendy_team_capture_and_share", // go/trendy/manage/engineers/5644523746787328 trendy_team_id: "5644523746787328", } team { name: "trendy_team_keep", // go/trendy/manage/engineers/5839518271668224 trendy_team_id: "5839518271668224", } team { name: "trendy_team_aaos_security", // go/trendy/manage/engineers/6264394363076608 trendy_team_id: "6264394363076608", } team { name: "trendy_team_zero_jank", // go/trendy/manage/engineers/4764874133897216 trendy_team_id: "4764874133897216", } team { name: "trendy_team_android_unified_core_infrastructure", // go/trendy/manage/engineers/5842172961914880 trendy_team_id: "5842172961914880", } team { name: "trendy_team_android_media_codec_framework", // go/trendy/manage/engineers/4943966050844672 trendy_team_id: "4943966050844672", } team { name: "trendy_team_system_gn_", // go/trendy/manage/engineers/4785636376444928 trendy_team_id: "4785636376444928", } team { name: "trendy_team_deprecated_android_auth_client", // go/trendy/manage/engineers/5471731632177152 trendy_team_id: "5471731632177152", } team { name: "trendy_team_wear_wear_ios_companion_sdk", // go/trendy/manage/engineers/5737044865089536 trendy_team_id: "5737044865089536", } team { name: "trendy_team_nearby", // go/trendy/manage/engineers/4959908969447424 trendy_team_id: "4959908969447424", } team { name: "trendy_team_camerax_make_pixel_", // go/trendy/manage/engineers/4521753585778688 trendy_team_id: "4521753585778688", } team { name: "trendy_team_wear_wear_health_services", // go/trendy/manage/engineers/6526182686097408 trendy_team_id: "6526182686097408", } team { name: "trendy_team_media_volume", // go/trendy/manage/engineers/6360142070841344 trendy_team_id: "6360142070841344", } team { name: "trendy_team_large_screen_experiences_sysui", // go/trendy/manage/engineers/5855214130069504 trendy_team_id: "5855214130069504", } team { name: "trendy_team_mainline_engprod", // go/trendy/manage/engineers/5474634789847040 trendy_team_id: "5474634789847040", } team { name: "trendy_team_wear_wear_power_foundations", // go/trendy/manage/engineers/6292909196214272 trendy_team_id: "6292909196214272", } team { name: "trendy_team_windowing_tools", // go/trendy/manage/engineers/6382778382188544 trendy_team_id: "6382778382188544", } team { name: "trendy_team_android_framework_appcompat", // go/trendy/manage/engineers/5383770701955072 trendy_team_id: "5383770701955072", } team { name: "trendy_team_fitbit", // go/trendy/manage/engineers/6497885327360000 trendy_team_id: "6497885327360000", } team { name: "trendy_team_overdrive", // go/trendy/manage/engineers/4961558236889088 trendy_team_id: "4961558236889088", } team { name: "trendy_team_framework_android_packages", // go/trendy/manage/engineers/5989762407104512 trendy_team_id: "5989762407104512", } team { name: "trendy_team_tkilbourn_team", // go/trendy/manage/engineers/4856646707871744 trendy_team_id: "4856646707871744", } team { name: "trendy_team_large_screen_experiences_platform", // go/trendy/manage/engineers/4826462937317376 trendy_team_id: "4826462937317376", } team { name: "trendy_team_pixel_system_service", // go/trendy/manage/engineers/5802643790135296 trendy_team_id: "5802643790135296", } team { name: "trendy_team_android_developer_tools", // go/trendy/manage/engineers/6201807353020416 trendy_team_id: "6201807353020416", } team { name: "trendy_team_autofill", // go/trendy/manage/engineers/4676203460329472 trendy_team_id: "4676203460329472", } team { name: "trendy_team_wsd_w5", // go/trendy/manage/engineers/4627306702045184 trendy_team_id: "4627306702045184", } team { name: "trendy_team_wear_wear_xfood_xwear_and_logistics", // go/trendy/manage/engineers/6599089311350784 trendy_team_id: "6599089311350784", } team { name: "trendy_team_android_media_drm", // go/trendy/manage/engineers/5311752690335744 trendy_team_id: "5311752690335744", } team { name: "trendy_team_nsylvain_team", // go/trendy/manage/engineers/5129062893912064 trendy_team_id: "5129062893912064", } team { name: "trendy_team_fwk_wifi_hal", // go/trendy/manage/engineers/5470082364735488 trendy_team_id: "5470082364735488", } team { name: "trendy_team_tombergan_team", // go/trendy/manage/engineers/5764031194497024 trendy_team_id: "5764031194497024", } team { name: "trendy_team_wear_wear_platform_program_partner_eng_", // go/trendy/manage/engineers/5472261070815232 trendy_team_id: "5472261070815232", } team { name: "trendy_team_masd_pixel_key_experiences", // go/trendy/manage/engineers/4873597306667008 trendy_team_id: "4873597306667008", } team { name: "trendy_team_external", // go/trendy/manage/engineers/6033032318156800 trendy_team_id: "6033032318156800", } team { name: "trendy_team_fwk_telephony", // go/trendy/manage/engineers/5663596411224064 trendy_team_id: "5663596411224064", } team { name: "trendy_team_customization_picker", // go/trendy/manage/engineers/6311142173081600 trendy_team_id: "6311142173081600", } team { name: "trendy_team_android_test_surfaces", // go/trendy/manage/engineers/4879149651099648 trendy_team_id: "4879149651099648", } team { name: "trendy_team_instant_apps", // go/trendy/manage/engineers/6720776841330688 trendy_team_id: "6720776841330688", } team { name: "trendy_team_accessibility_aas", // go/trendy/manage/engineers/6043198228299776 trendy_team_id: "6043198228299776", } team { name: "trendy_team_android_engprod_lon", // go/trendy/manage/engineers/5432243163791360 trendy_team_id: "5432243163791360", } team { name: "trendy_team_pmw_mce", // go/trendy/manage/engineers/6424723868909568 trendy_team_id: "6424723868909568", } team { name: "trendy_team_pixel_system_sw_kernel", // go/trendy/manage/engineers/5436802711846912 trendy_team_id: "5436802711846912", } team { name: "trendy_team_nga", // go/trendy/manage/engineers/5594876934488064 trendy_team_id: "5594876934488064", } team { name: "trendy_team_web_on_android_performance", // go/trendy/manage/engineers/5864851748847616 trendy_team_id: "5864851748847616", } team { name: "trendy_team_reveman_team", // go/trendy/manage/engineers/5113274057261056 trendy_team_id: "5113274057261056", } team { name: "trendy_team_test_eng_ota_framework_sysui_suw_abvt_cts", // go/trendy/manage/engineers/4653694981111808 trendy_team_id: "4653694981111808", } team { name: "trendy_team_backup_restore", // go/trendy/manage/engineers/5049519167111168 trendy_team_id: "5049519167111168", } team { name: "trendy_team_pixel_system_sw_bringup_and_factory", // go/trendy/manage/engineers/5999752665268224 trendy_team_id: "5999752665268224", } team { name: "trendy_team_wear_wear_calling_messaging", // go/trendy/manage/engineers/5401274807648256 trendy_team_id: "5401274807648256", } team { name: "trendy_team_android_imaging", // go/trendy/manage/engineers/5838538113384448 trendy_team_id: "5838538113384448", } team { name: "trendy_team_pixel_system_sw_sensor", // go/trendy/manage/engineers/4904417557643264 trendy_team_id: "4904417557643264", } team { name: "trendy_team_dogfood_triage", // go/trendy/manage/engineers/5130823981236224 trendy_team_id: "5130823981236224", } team { name: "trendy_team_input_method_framework", // go/trendy/manage/engineers/6394201770459136 trendy_team_id: "6394201770459136", } team { name: "trendy_team_wear_wear_platform_dev_lead_device_program_", // go/trendy/manage/engineers/4642393194135552 trendy_team_id: "4642393194135552", } team { name: "trendy_team_wear_bona_companion", // go/trendy/manage/engineers/4721932784009216 trendy_team_id: "4721932784009216", } team { name: "trendy_team_wear_wear_compose", // go/trendy/manage/engineers/4958404271308800 trendy_team_id: "4958404271308800", } team { name: "trendy_team_system_fugu_", // go/trendy/manage/engineers/5682837864710144 trendy_team_id: "5682837864710144", } team { name: "trendy_team_tvolkert_team", // go/trendy/manage/engineers/5093014696525824 trendy_team_id: "5093014696525824", } team { name: "trendy_team_media_framework_drm", // go/trendy/manage/engineers/5311752690335744 trendy_team_id: "5311752690335744", } team { name: "trendy_team_media_framework_audio", // go/trendy/manage/engineers/5823575353065472 trendy_team_id: "5823575353065472", } team { name: "trendy_team_pixel_pearl", // go/trendy/manage/engineers/6326219602231296 trendy_team_id: "6326219602231296", } team { name: "trendy_team_ar_sensors_context_hub", // go/trendy/manage/engineers/4776371090259968 trendy_team_id: "4776371090259968", } team { name: "trendy_team_media_codec_framework", // go/trendy/manage/engineers/4943966050844672 trendy_team_id: "4943966050844672", } team { name: "trendy_team_android_platform_performance_testing", // go/trendy/manage/engineers/5810097836621824 trendy_team_id: "5810097836621824", } team { name: "trendy_team_adte", // go/trendy/manage/engineers/5551098528825344 trendy_team_id: "5551098528825344", } team { name: "trendy_team_incremental", // go/trendy/manage/engineers/5955405559201792 trendy_team_id: "5955405559201792", } team { name: "trendy_team_android_media_better_together", // go/trendy/manage/engineers/5617300451721216 trendy_team_id: "5617300451721216", } team { name: "trendy_team_attack_tools", // go/trendy/manage/engineers/4705629185081344 trendy_team_id: "4705629185081344", } team { name: "trendy_team_android_media_solutions_editing", // go/trendy/manage/engineers/5350750192762880 trendy_team_id: "5350750192762880", } team { name: "trendy_team_android_media_solutions_playback", // go/trendy/manage/engineers/6742515252559872 trendy_team_id: "6742515252559872", } team { name: "trendy_team_android_telemetry_client_infra", // go/trendy/manage/engineers/5403245077430272 trendy_team_id: "5403245077430272", } team { name: "trendy_team_pte_sysui", // go/trendy/manage/engineers/5185897463382016 trendy_team_id: "5185897463382016", } team { name: "trendy_team_pixel_troubleshooting_app", // go/trendy/manage/engineers/5097003746426880 trendy_team_id: "5097003746426880", } team { name: "trendy_team_desktop_firmware", // go/trendy/manage/engineers/5787938454863872 trendy_team_id: "5787938454863872", } team { name: "trendy_team_art_cloud", // go/trendy/manage/engineers/5121440647577600 trendy_team_id: "5121440647577600", } team { name: "trendy_team_ravenwood", // go/trendy/manage/engineers/6027181500497920 trendy_team_id: "6027181500497920", } team { name: "trendy_team_automotive_cast", // go/trendy/manage/engineers/5293683026264064 trendy_team_id: "5293683026264064", } team { name: "trendy_team_wear_standalone_kids", // go/trendy/manage/engineers/6303298703949824 trendy_team_id: "6303298703949824", } team { name: "trendy_team_desktop_stats", // go/trendy/manage/engineers/5440764114206720 trendy_team_id: "5440764114206720", } team { name: "trendy_team_desktop_wifi", // go/trendy/manage/engineers/6463689697099776 trendy_team_id: "6463689697099776", } // DON'T ADD NEW RULES HERE. For more details refer to // go/new-android-ownership-model ================================================ FILE: teams/OWNERS ================================================ dariofreni@google.com ronish@google.com ================================================ FILE: tests/artifact_path_requirements/inherit1.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit3.rbc", _inherit3_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/inherit3", _inherit3_init) ================================================ FILE: tests/artifact_path_requirements/inherit2.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit4.rbc", _inherit4_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/inherit4", _inherit4_init) rblf.require_artifacts_in_path(handle, "vendor/", "") ================================================ FILE: tests/artifact_path_requirements/inherit3.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit4.rbc", _inherit4_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/inherit4", _inherit4_init) rblf.require_artifacts_in_path(handle, "vendor/", "") ================================================ FILE: tests/artifact_path_requirements/inherit4.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) rblf.setdefault(handle, "PRODUCT_COPY_FILES") cfg["PRODUCT_COPY_FILES"] += ["foo/bar/baz.txt:vendor/etc/baz.txt"] ================================================ FILE: tests/artifact_path_requirements/product.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit1.rbc", _inherit1_init = "init") load(":inherit2.rbc", _inherit2_init = "init") load(":inherit3.rbc", _inherit3_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/inherit1", _inherit1_init) rblf.inherit(handle, "test/inherit2", _inherit2_init) rblf.inherit(handle, "test/inherit3", _inherit3_init) ================================================ FILE: tests/artifact_path_requirements/test.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load("//build/make/tests/input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) def test(): (globals, globals_base) = rblf.product_configuration("test/product", init, input_variables_init) assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/product.mk.PRODUCT_COPY_FILES"]) assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/inherit2.mk.PRODUCT_COPY_FILES"]) assert_eq(["foo/bar/baz.txt:vendor/etc/baz.txt"], globals["PRODUCTS.test/inherit3.mk.PRODUCT_COPY_FILES"]) ================================================ FILE: tests/b_tests.sh ================================================ # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # These commands are expected to always return successfully trap 'exit 1' ERR source $(dirname $0)/../envsetup.sh # lunch required to set up PATH to use b lunch aosp_arm64 test_target=//build/bazel/scripts/difftool:difftool if b build //build/bazel:nonexistent_module &>/dev/null ; then echo "b did not fail when building a nonexistent module" >&2 exit 1 fi b build "$test_target" b build -- "$test_target" b build "$test_target" --run-soong-tests b build --run-soong-tests "$test_target" b --run-soong-tests build "$test_target" # Test that the bazel server can be restarted once shut down. If run in a # docker container, you need to run the docker container with --init or # have some other process as PID 1 that can reap zombies. b shutdown b cquery 'kind(test, //build/bazel/examples/android_app/...)' --config=android b run $test_target -- --help >/dev/null # Workflow tests for bmod bmod libm b run $(bmod fastboot) -- help b build $(bmod libm) $(bmod libcutils) --config=android ================================================ FILE: tests/board.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g["A_LIST_VARIABLE"] += ["bar"] ================================================ FILE: tests/board_input_vars.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g["A_LIST_VARIABLE"] = ["foo"] ================================================ FILE: tests/conversion_error.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Run test configuration and verify its result. # The main configuration file is device.rbc. # It inherits part1.rbc and also includes include1.rbc # TODO(asmundak): more tests are needed to verify that: # * multi-level inheritance works as expected # * all runtime functions (wildcard, regex, etc.) work load("//build/make/core:product_config.rbc", "rblf") load(":version_defaults.rbc", "version_defaults") load(":device.rbc", "init") rblf.mk2rbc_error("file.mk:123", "cannot convert") ================================================ FILE: tests/envsetup_tests.sh ================================================ #!/bin/bash -e # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. tests=( $(dirname $0)/lunch_tests.sh ) for test in $tests; do bash -x $test done ================================================ FILE: tests/include1.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Included file (not inherited) # Converted from makefile ### PRODUCT_PACKAGES += inc load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) rblf.setdefault(handle, "PRODUCT_PACKAGES") cfg["PRODUCT_PACKAGES"] += ["inc"] ================================================ FILE: tests/inherits_in_regular_variables/inherit1.rbc ================================================ # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) cfg.setdefault("PRODUCT_PACKAGES", []) cfg["PRODUCT_PACKAGES"] += ["bar"] ================================================ FILE: tests/inherits_in_regular_variables/product.rbc ================================================ # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit1.rbc", _inherit1_init = "init") def init(g, handle): cfg = rblf.cfg(handle) cfg.setdefault("PRODUCT_PACKAGES", []) cfg["PRODUCT_PACKAGES"] += ["foo"] g["PRODUCT_PACKAGES_COPY"] = cfg["PRODUCT_PACKAGES"] rblf.inherit(handle, "test/inherit1", _inherit1_init) ================================================ FILE: tests/inherits_in_regular_variables/test.rbc ================================================ # Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load("//build/make/tests/input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) def test(): (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) assert_eq(["foo", "bar"], globals["PRODUCTS.test/device.mk.PRODUCT_PACKAGES"]) assert_eq(["foo", ("test/inherit1",)], globals["PRODUCT_PACKAGES_COPY"]) # Ideally we would check that rblf.printvars returns the correct result, but we don't have # a good way to intercept its output or mock rblf_cli ================================================ FILE: tests/input_variables.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This file was generated by running `m RBC_PRODUCT_CONFIG=1 nothing` # and then copying it from out/rbc/out/rbc/make_vars_pre_product_config.rbc. # It was manually trimmed down afterwards to just the variables we need. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g["PLATFORM_VERSION_CODENAME"] = "Tiramisu" g["PLATFORM_VERSION"] = "Tiramisu" g["TARGET_BUILD_VARIANT"] = "userdebug" g["TARGET_BUILD_TYPE"] = "release" g["TARGET_PRODUCT"] = "aosp_arm64" g["PLATFORM_SDK_VERSION"] = "31" ================================================ FILE: tests/lunch_tests.sh ================================================ #!/usr/bin/env bash # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. source $(dirname $0)/../envsetup.sh unset TARGET_PRODUCT TARGET_BUILD_VARIANT TARGET_PLATFORM_VERSION function check_lunch ( echo lunch $1 set +e lunch $1 > /dev/null 2> /dev/null set -e [ "$TARGET_PRODUCT" = "$2" ] || ( echo "lunch $1: expected TARGET_PRODUCT='$2', got '$TARGET_PRODUCT'" && exit 1 ) [ "$TARGET_BUILD_VARIANT" = "$3" ] || ( echo "lunch $1: expected TARGET_BUILD_VARIANT='$3', got '$TARGET_BUILD_VARIANT'" && exit 1 ) [ "$TARGET_PLATFORM_VERSION" = "$4" ] || ( echo "lunch $1: expected TARGET_PLATFORM_VERSION='$4', got '$TARGET_PLATFORM_VERSION'" && exit 1 ) ) default_version=$(get_build_var RELEASE_PLATFORM_VERSION) # lunch tests check_lunch "aosp_arm64" "aosp_arm64" "eng" "" check_lunch "aosp_arm64-userdebug" "aosp_arm64" "userdebug" "" check_lunch "aosp_arm64-userdebug-$default_version" "aosp_arm64" "userdebug" "$default_version" check_lunch "abc" "" "" "" check_lunch "aosp_arm64-abc" "" "" "" check_lunch "aosp_arm64-userdebug-abc" "" "" "" check_lunch "aosp_arm64-abc-$default_version" "" "" "" check_lunch "abc-userdebug-$default_version" "" "" "" check_lunch "-" "" "" "" check_lunch "--" "" "" "" check_lunch "-userdebug" "" "" "" check_lunch "-userdebug-" "" "" "" check_lunch "-userdebug-$default_version" "" "" "" check_lunch "aosp_arm64-userdebug-$default_version-" "" "" "" check_lunch "aosp_arm64-userdebug-$default_version-abc" "" "" "" ================================================ FILE: tests/part1.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Part configuration # Converted from ### PRODUCT_COPY_FILES += part_from:part_to ### PRODUCT_PRODUCT_PROPERTIES += part_properties load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) rblf.setdefault(handle, "PRODUCT_COPY_FILES") cfg["PRODUCT_COPY_FILES"] += ["part_from:part_to"] rblf.setdefault(handle, "PRODUCT_PRODUCT_PROPERTIES") cfg["PRODUCT_PRODUCT_PROPERTIES"] += ["part_properties"] rblf.soong_config_namespace(g, "NS1") rblf.soong_config_append(g, "NS1", "v1", "abc_part1") ================================================ FILE: tests/prefixed_sort_order/base-secondary.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g.setdefault("MY_VAR", []) g["MY_VAR"] += ["foo"] ================================================ FILE: tests/prefixed_sort_order/base.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g.setdefault("MY_VAR", []) g["MY_VAR"] += ["bar"] ================================================ FILE: tests/prefixed_sort_order/product.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":base.rbc", _base_init = "init") load(":base-secondary.rbc", _base_secondary_init = "init") def init(g, handle): cfg = rblf.cfg(handle) # It's important that base-secondary uses a dash, an underscore won't expose the sort order issue: # >>> sorted(["base", "base-secondary"]) # ['base', 'base-secondary'] # >>> sorted(["base.mk", "base-secondary.mk"]) # ['base-secondary.mk', 'base.mk'] rblf.inherit(handle, "base", _base_init) rblf.inherit(handle, "base-secondary", _base_secondary_init) ================================================ FILE: tests/prefixed_sort_order/test.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load("//build/make/tests/input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) def test(): (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) assert_eq(["foo", "bar"], globals["MY_VAR"]) ================================================ FILE: tests/product.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Top-level test configuration. # Converted from the following makefile ### PRODUCT_PACKAGES += dev ### PRODUCT_HOST_PACKAGES += host ### $(call inherit-product, $(LOCAL_PATH)/part1.mk) ### PRODUCT_COPY_FILES += device_from:device_to ### include $(LOCAL_PATH)/include1.mk ### PRODUCT_PACKAGES += dev_after ### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz:/etc/xyz ### PRODUCT_COPY_FILES += $(call copy-files,x.xml y.xml,/etc) ### $(call add_soong_config_namespace,NS1) ### $(call soong_config_append,NS1,v1,abc) ### $(call soong_config_append,NS1,v2,def) ### $(call add_soong_config_var_value,NS2,v3,abc) ### $(call soong_config_set,NS2,v3,xyz) load("//build/make/core:product_config.rbc", "rblf") load(":part1.rbc", _part1_init = "init") load(":include1.rbc", _include1_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.setdefault(handle, "PRODUCT_PACKAGES") cfg["PRODUCT_PACKAGES"] += ["dev"] rblf.setdefault(handle, "PRODUCT_HOST_PACKAGES") cfg["PRODUCT_HOST_PACKAGES"] += ["host"] rblf.inherit(handle, "test/part1", _part1_init) rblf.setdefault(handle, "PRODUCT_COPY_FILES") cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"] _include1_init(g, handle) cfg["PRODUCT_PACKAGES"] += ["dev_after"] cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin", "||VENDOR-PATH-PH||/etc") + ["xyz:/etc/xyz"]) cfg["PRODUCT_COPY_FILES"] += rblf.copy_files("x.xml y.xml", "/etc") cfg["PRODUCT_COPY_FILES"] += rblf.copy_files(["from/sub/x", "from/sub/y"], "to") rblf.soong_config_namespace(g, "NS1") rblf.soong_config_append(g, "NS1", "v1", "abc") rblf.soong_config_append(g, "NS1", "v2", "def") rblf.soong_config_set(g, "NS2", "v3", "abc") rblf.soong_config_set(g, "NS2", "v3", "xyz") rblf.soong_config_set(g, "NS2", "v4", "xyz ") rblf.mkdist_for_goals(g, "goal", "dir1/file1:out1 dir1/file2:out2") rblf.mkdist_for_goals(g, "goal", "dir2/file2:") if rblf.board_platform_in(g, "board1 board2"): cfg["PRODUCT_PACKAGES"] += ["bad_package"] g["TARGET_BOARD_PLATFORM"] = "board1" if rblf.board_platform_in(g, "board1 board2"): cfg["PRODUCT_PACKAGES"] += ["board1_in"] if rblf.board_platform_in(g, ["board3","board2"]): cfg["PRODUCT_PACKAGES"] += ["bad_board_in"] if rblf.board_platform_is(g, "board1"): cfg["PRODUCT_PACKAGES"] += ["board1_is"] if rblf.board_platform_is(g, "board2"): cfg["PRODUCT_PACKAGES"] += ["bad_board1_is"] ================================================ FILE: tests/roboleaf_tests.sh ================================================ #!/bin/bash -e # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. tests=( $(dirname $0)/b_tests.sh ) for test in $tests; do bash -x $test done ================================================ FILE: tests/run.rbc ================================================ # Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Run test product configuration and verify its result. # The main configuration file is product.rbc. # It inherits part1.rbc and also includes include1.rbc # TODO(asmundak): more tests are needed to verify that: # * multi-level inheritance works as expected # * all runtime functions (wildcard, regex, etc.) work load("//build/make/core:product_config.rbc", "rblf") load(":input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") load(":board.rbc", board_init = "init") load(":board_input_vars.rbc", board_input_vars_init = "init") load("//build/make/tests/single_value_inheritance:test.rbc", test_single_value_inheritance = "test") load("//build/make/tests/single_value_inheritance_2:test.rbc", test_single_value_inheritance_2 = "test") load("//build/make/tests/artifact_path_requirements:test.rbc", test_artifact_path_requirements = "test") load("//build/make/tests/prefixed_sort_order:test.rbc", test_prefixed_sort_order = "test") load("//build/make/tests/inherits_in_regular_variables:test.rbc", test_inherits_in_regular_variables = "test") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) def assert_dict_subset(expected, actual): for key, val in expected.items(): assert_eq(val, actual[key]) # Unit tests for non-trivial runtime functions assert_eq(["a", "b", "c"], rblf.mksort("b a c c")) assert_eq(["a", "b", "c"], rblf.mksort(["b", "a", "c", "c"])) assert_eq("", rblf.mkstrip(" \n \t ")) assert_eq("a b c", rblf.mkstrip(" a b \n c \t")) assert_eq("1", rblf.mkstrip("1 ")) assert_eq(["a", "b"], rblf.words("a b")) assert_eq(["a", "b", "c"], rblf.words(["a b", "c"])) # 1-tuple like we use in product variables assert_eq(["a b", ("c",)], rblf.words(["a b", ("c",)])) assert_eq("b1 b2", rblf.mksubst("a", "b", "a1 a2")) assert_eq(["b1", "x2"], rblf.mksubst("a", "b", ["a1", "x2"])) assert_eq("ABcdYZ", rblf.mkpatsubst("ab%yz", "AB%YZ", "abcdyz")) assert_eq("bcz", rblf.mkpatsubst("a%z", "A%Z", "bcz")) assert_eq(["Ay", "Az"], rblf.mkpatsubst("a%", "A%", ["ay", "az"])) assert_eq("AcZ bcz", rblf.mkpatsubst("a%z", "A%Z", "acz bcz")) assert_eq("Abcd", rblf.mkpatsubst("a%", "A%", "abcd")) assert_eq("abcZ", rblf.mkpatsubst("%z", "%Z", "abcz")) assert_eq("azx b", rblf.mkpatsubst("az", "AZ", "azx b")) assert_eq(["azx", "b"], rblf.mkpatsubst("az", "AZ", ["azx", "b"])) assert_eq("ABC", rblf.mkpatsubst("abc", "ABC", "abc")) assert_eq(["%/foo"], rblf.mkpatsubst("%", "\\%/%", ["foo"])) assert_eq(["foo/%"], rblf.mkpatsubst("%", "%/%", ["foo"])) assert_eq(["from/a:to/a", "from/b:to/b"], rblf.product_copy_files_by_pattern("from/%", "to/%", "a b")) assert_eq([], rblf.filter(["a", "", "b"], "f")) assert_eq(["ab%c", "axyzb%c"], rblf.filter(["a%b%c"], ["ab%c", "axyzb%c", "axyzb%cd", "axyzbwc"])) assert_eq(["abc", "bcd"], rblf.filter(["a%", "b%"], ["abc", "def", "bcd", "xabc"])) assert_eq(["b", "ab"], rblf.filter_out(["a", "" ], ["a", "", "b", "ab"])) assert_eq(["c"], rblf.filter_out(["a", "b" ], ["a", "b", "c"])) assert_eq(["c"], rblf.filter_out(["a%", "b" ], ["abc", "b", "c"])) assert_eq("foo.c no_folder", rblf.notdir(["src/foo.c", "no_folder"])) assert_eq("foo.c no_folder", rblf.notdir("src/foo.c no_folder")) assert_eq("", rblf.notdir("/")) assert_eq("", rblf.notdir("")) cwd = rblf_shell('pwd') assert_eq(cwd+"/foo/bar", rblf.abspath("foo/bar")) assert_eq(cwd+"/bar", rblf.abspath("foo/.././bar")) assert_eq(cwd+"/bar", rblf.abspath("foo/..////bar//")) assert_eq("/foo/baz", rblf.abspath("/foo/bar/../baz")) assert_eq(cwd+"/foo/bar "+cwd+"/foo/baz", rblf.abspath("foo/bar foo/baz")) assert_eq("/baz", rblf.abspath("/../../../../../../../../../../../../../../../../baz")) assert_eq("foo", rblf.first_word("foo bar")) assert_eq("foo", rblf.first_word(["foo", "bar"])) assert_eq("", rblf.first_word("")) assert_eq("", rblf.first_word([])) assert_eq("bar", rblf.last_word("foo bar")) assert_eq("bar", rblf.last_word(["foo", "bar"])) assert_eq("", rblf.last_word("")) assert_eq("", rblf.last_word([])) assert_eq(["foo", "bar"], rblf.flatten_2d_list([["foo", "bar"]])) assert_eq(["foo", "bar"], rblf.flatten_2d_list([["foo"], ["bar"]])) assert_eq([], rblf.flatten_2d_list([])) assert_eq( ["build/make/tests/board.rbc", "build/make/tests/board_input_vars.rbc"], rblf.expand_wildcard("build/make/tests/board*.rbc") ) assert_eq( ["build/make/tests/run.rbc", "build/make/tests/product.rbc"], rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/product.rbc") ) assert_eq( ["build/make/tests/run.rbc"], rblf.expand_wildcard("build/make/tests/run.rbc build/make/tests/nonexistent.rbc") ) (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) assert_dict_subset({ "PRODUCTS.test/device.mk.PRODUCT_COPY_FILES": [ "part_from:part_to", "device_from:device_to", "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml", "xyz:/etc/xyz", "x.xml:/etc/x.xml", "y.xml:/etc/y.xml", "from/sub/x:to/x", "from/sub/y:to/y", ], "PRODUCTS.test/device.mk.PRODUCT_HOST_PACKAGES": ["host"], "PRODUCTS.test/device.mk.PRODUCT_PACKAGES": [ "dev", "inc", "dev_after", "board1_in", "board1_is", ], "PRODUCTS.test/device.mk.PRODUCT_PRODUCT_PROPERTIES": ["part_properties"] }, globals) ns = globals["$SOONG_CONFIG_NAMESPACES"] assert_eq( { "NS1": { "v1": "abc abc_part1", "v2": "def" }, "NS2": { "v3": "xyz", "v4": "xyz" } }, {k:v for k, v in sorted(ns.items()) } ) assert_eq("Tiramisu", globals["PLATFORM_VERSION"]) assert_eq("31", globals["PLATFORM_SDK_VERSION"]) assert_eq("xyz", rblf.soong_config_get(globals, "NS2", "v3")) assert_eq(None, rblf.soong_config_get(globals, "NS2", "nonexistant_var")) goals = globals["$dist_for_goals"] assert_eq( { "goal": [("dir1/file1", "out1"), ("dir1/file2", "out2"), ("dir2/file2", "file2")] }, { k:v for k,v in sorted(goals.items()) } ) (board_globals, board_globals_base) = rblf.board_configuration(board_init, board_input_vars_init) assert_eq({"A_LIST_VARIABLE": ["foo", "bar"]}, board_globals) assert_eq({"A_LIST_VARIABLE": ["foo"]}, board_globals_base) g = {"FOO": "a", "BAR": "c", "BAZ": "e"} cfg = {"FOO": "b", "BAR": "d", "BAZ": "f"} rblf.clear_var_list(g, struct(cfg=cfg), "FOO BAR NEWVAR") assert_eq("", g["FOO"]) assert_eq("", cfg["FOO"]) assert_eq("", g["BAR"]) assert_eq("", cfg["BAR"]) assert_eq("e", g["BAZ"]) assert_eq("f", cfg["BAZ"]) assert_eq("", g.get("NEWVAR")) test_single_value_inheritance() test_single_value_inheritance_2() test_artifact_path_requirements() test_prefixed_sort_order() test_inherits_in_regular_variables() ================================================ FILE: tests/single_value_inheritance/inherit1.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) cfg["PRODUCT_CHARACTERISTICS"] = "tablet" cfg["PRODUCT_DEFAULT_DEV_CERTIFICATE"] = "vendor/myvendor/certs/devkeys/devkey" cfg.setdefault("PRODUCT_PACKAGES", []) cfg["PRODUCT_PACKAGES"] += ["bar"] ================================================ FILE: tests/single_value_inheritance/inherit2.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) cfg["PRODUCT_CHARACTERISTICS"] = "nosdcard" cfg.setdefault("PRODUCT_PACKAGES", []) cfg["PRODUCT_PACKAGES"] += ["foo"] ================================================ FILE: tests/single_value_inheritance/product.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":inherit1.rbc", _inherit1_init = "init") load(":inherit2.rbc", _inherit2_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/inherit2", _inherit2_init) rblf.inherit(handle, "test/inherit1", _inherit1_init) cfg["PRODUCT_DEFAULT_DEV_CERTIFICATE"] = "" ================================================ FILE: tests/single_value_inheritance/test.rbc ================================================ # Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load("//build/make/tests/input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) def test(): (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) assert_eq("tablet", globals["PRODUCTS.test/device.mk.PRODUCT_CHARACTERISTICS"]) assert_eq("vendor/myvendor/certs/devkeys/devkey", globals["PRODUCTS.test/device.mk.PRODUCT_DEFAULT_DEV_CERTIFICATE"]) assert_eq(["foo", "bar"], globals["PRODUCTS.test/device.mk.PRODUCT_PACKAGES"]) ================================================ FILE: tests/single_value_inheritance_2/a.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) cfg["PRODUCT_ENABLE_UFFD_GC"] = "true" ================================================ FILE: tests/single_value_inheritance_2/b.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) cfg["PRODUCT_ENABLE_UFFD_GC"] = "default" ================================================ FILE: tests/single_value_inheritance_2/c.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":b.rbc", _b_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/b", _b_init) ================================================ FILE: tests/single_value_inheritance_2/d.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":c.rbc", _c_init = "init") load(":a.rbc", _a_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/a", _a_init) rblf.inherit(handle, "test/c", _c_init) ================================================ FILE: tests/single_value_inheritance_2/product.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load(":b.rbc", _b_init = "init") load(":d.rbc", _d_init = "init") def init(g, handle): cfg = rblf.cfg(handle) rblf.inherit(handle, "test/b", _b_init) rblf.inherit(handle, "test/d", _d_init) ================================================ FILE: tests/single_value_inheritance_2/test.rbc ================================================ # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load("//build/make/core:product_config.rbc", "rblf") load("//build/make/tests/input_variables.rbc", input_variables_init = "init") load(":product.rbc", "init") def assert_eq(expected, actual): if expected != actual: fail("Expected '%s', got '%s'" % (expected, actual)) # This test is testing that single value variables are "stolen" when processing the inheritance # graph. i.e. if you have a graph like this: # # B A # |\ | # | C | # \ \| # \ D # \| # E # # The same variable is defined in both A and B. In D, the value from A is chosen because it comes # alphabetically before C. But then in E, the value from D is chosen instead of the value from B, # because the value of B was "stolen" and sucked into C, leaving B with no value set. def test(): (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init) assert_eq("true", globals["PRODUCTS.test/device.mk.PRODUCT_ENABLE_UFFD_GC"]) ================================================ FILE: tests/version_defaults.rbc ================================================ version_defaults = struct( codenames = { "SP1A" : "S" }, default_platform_version = "SP1A", max_platform_version = "SP1A", min_platform_version = "SP1A", platform_base_sdk_extension_version = 0, platform_sdk_extension_version = 1, platform_sdk_version = 30, platform_security_patch = "2021-08-05", platform_version_last_stable = 11, ) ================================================ FILE: tools/Android.bp ================================================ // Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } python_binary_host { name: "generate-self-extracting-archive", srcs: ["generate-self-extracting-archive.py"], } python_binary_host { name: "post_process_props", srcs: ["post_process_props.py"], libs: [ "uffd_gc_utils", ], } python_test_host { name: "post_process_props_unittest", main: "test_post_process_props.py", srcs: [ "post_process_props.py", "test_post_process_props.py", ], libs: [ "uffd_gc_utils", ], test_config: "post_process_props_unittest.xml", test_suites: ["general-tests"], } python_binary_host { name: "extract_kernel", srcs: ["extract_kernel.py"], } genrule_defaults { name: "extract_kernel_release_defaults", tools: [ "extract_kernel", "lz4", ], out: ["kernel_release.txt"], cmd: "$(location) --tools lz4:$(location lz4) --input $(in) --output-release > $(out)", } cc_binary_host { name: "build-runfiles", srcs: ["build-runfiles.cc"], } python_binary_host { name: "check_radio_versions", srcs: ["check_radio_versions.py"], } python_binary_host { name: "check_elf_file", srcs: ["check_elf_file.py"], } python_binary_host { name: "generate_gts_shared_report", srcs: ["generate_gts_shared_report.py"], } python_binary_host { name: "list_files", main: "list_files.py", srcs: [ "list_files.py", ], } python_test_host { name: "auto_gen_test_config_test", main: "auto_gen_test_config_test.py", srcs: [ "auto_gen_test_config.py", "auto_gen_test_config_test.py", ], auto_gen_config: true, test_suites: ["general-tests"], test_options: { unit_test: true, }, } python_binary_host { name: "characteristics_rro_generator", srcs: ["characteristics_rro_generator.py"], } python_binary_host { name: "merge-event-log-tags", srcs: [ "event_log_tags.py", "merge-event-log-tags.py", ], } python_binary_host { name: "java-event-log-tags", srcs: [ "event_log_tags.py", "java-event-log-tags.py", ], } ================================================ FILE: tools/aconfig/.editorconfig ================================================ # EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true [*.java] indent_style = tab indent_size = 4 ================================================ FILE: tools/aconfig/.gitignore ================================================ /Cargo.lock /target ================================================ FILE: tools/aconfig/Cargo.toml ================================================ [workspace] members = [ "aconfig", "aconfig_device_paths", "aconfig_protos", "aconfig_storage_file", "aconfig_storage_read_api", "aconfig_storage_write_api", "aflags", "convert_finalized_flags", "exported_flag_check", ] resolver = "2" ================================================ FILE: tools/aconfig/MODULE_LICENSE_APACHE2 ================================================ ================================================ FILE: tools/aconfig/OWNERS ================================================ dzshen@google.com opg@google.com zhidou@google.com amhk@google.com #{LAST_RESORT_SUGGESTION} jham@google.com #{LAST_RESORT_SUGGESTION} joeo@google.com #{LAST_RESORT_SUGGESTION} ================================================ FILE: tools/aconfig/PREUPLOAD.cfg ================================================ [Builtin Hooks] rustfmt = true [Builtin Hooks Options] rustfmt = --config-path=rustfmt.toml ================================================ FILE: tools/aconfig/TEST_MAPPING ================================================ { "presubmit": [ { // aconfig unit tests "name": "aconfig.test" }, { // aconfig Java integration tests (host) "name": "AconfigJavaHostTest" }, { // aconfig Java integration tests "name": "aconfig.test.java" }, { // aconfig C++ integration tests (production mode auto-generated code) "name": "aconfig.test.cpp" }, { // aconfig C++ integration tests (test mode auto-generated code) "name": "aconfig.test.cpp.test_mode" }, // TODO(b/327420679): Enable export mode for native flag library // { // // aconfig C++ integration tests (exported mode auto-generated code) // "name": "aconfig.test.cpp.exported_mode" // }, { // aconfig Rust integration tests (production mode auto-generated code) "name": "aconfig.prod_mode.test.rust" }, { // aconfig Rust integration tests (test mode auto-generated code) "name": "aconfig.test_mode.test.rust" }, // TODO(b/327420679): Enable export mode for native flag library // { // // aconfig Rust integration tests (exported mode auto-generated code) // "name": "aconfig.exported_mode.test.rust" // }, { // aflags CLI unit tests "name": "aflags.test" }, { // aconfig_protos unit tests "name": "aconfig_protos.test" }, { // aconfig_storage_file unit tests "name": "aconfig_storage_file.test" }, { // Ensure changes on aconfig auto generated library is compatible with // test testing filtering logic. Breakage on this test means all tests // that using the flag annotations to do filtering will get affected. "name": "FlagAnnotationTests", "options": [ { "include-filter": "android.cts.flags.tests.FlagAnnotationTest" } ] }, { // Ensure changes on aconfig auto generated library is compatible with // test testing filtering logic. Breakage on this test means all tests // that using the flag macros to do filtering will get affected. "name": "FlagMacrosTests" }, { // aconfig_storage_write_api unit tests "name": "aconfig_storage_write_api.test" }, { // aconfig_storage_read_api unit tests "name": "aconfig_storage_read_api.test" }, { // aconfig_storage write api rust integration tests "name": "aconfig_storage_write_api.test.rust" }, { // aconfig_storage write api cpp integration tests "name": "aconfig_storage_write_api.test.cpp" }, { // aconfig_storage read api rust integration tests "name": "aconfig_storage_read_api.test.rust" }, { // aconfig_storage read api cpp integration tests "name": "aconfig_storage_read_api.test.cpp" }, { // aconfig_storage file cpp integration tests "name": "aconfig_storage_file.test.cpp" }, { // aconfig_storage file java integration tests "name": "aconfig_storage_file.test.java" }, { // aconfig_storage read functional test "name": "aconfig_storage_read_functional" } ] } ================================================ FILE: tools/aconfig/aconfig/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "aconfig.defaults", edition: "2021", clippy_lints: "android", lints: "android", srcs: [ "src/main.rs", ":finalized_flags_record.json", ], rustlibs: [ "libaconfig_protos", "libaconfig_storage_file", "libanyhow", "libclap", "libitertools", "libprotobuf", "libserde", "libserde_json", "libtinytemplate", "libconvert_finalized_flags", ], cfgs: select(release_flag("RELEASE_ACONFIG_FINGERPRINT_RUST"), { true: ["enable_fingerprint_rust"], default: [], }), } rust_binary_host { name: "aconfig", defaults: ["aconfig.defaults"], } rust_test_host { name: "aconfig.test", defaults: ["aconfig.defaults"], rustlibs: [ "libitertools", ], test_suites: ["general-tests"], } // integration tests: general aconfig_declarations { name: "aconfig.test.flags", package: "com.android.aconfig.test", container: "system", srcs: ["tests/test.aconfig"], } aconfig_declarations { name: "aconfig.test.exported.flags", package: "com.android.aconfig.test.exported", exportable: true, container: "system", srcs: ["tests/test_exported.aconfig"], } aconfig_declarations { name: "aconfig.test.forcereadonly.flags", package: "com.android.aconfig.test.forcereadonly", container: "system", srcs: ["tests/test_force_read_only.aconfig"], } aconfig_values { name: "aconfig.test.flag.values", package: "com.android.aconfig.test", srcs: [ "tests/first.values", "tests/second.values", ], } aconfig_values { name: "aconfig.test.flag.second_values", package: "com.android.aconfig.test", srcs: [ "tests/third.values", ], } aconfig_value_set { name: "aconfig.test.flag.value_set", values: [ "aconfig.test.flag.values", ], } // integration tests: java java_aconfig_library { name: "aconfig_test_java_library", aconfig_declarations: "aconfig.test.flags", } java_aconfig_library { name: "aconfig_test_java_library_exported", aconfig_declarations: "aconfig.test.exported.flags", mode: "exported", } java_aconfig_library { name: "aconfig_test_java_library_forcereadonly", aconfig_declarations: "aconfig.test.forcereadonly.flags", mode: "force-read-only", } android_test { name: "aconfig.test.java", srcs: [ "tests/AconfigTest.java", ], manifest: "tests/AndroidManifest.xml", certificate: "platform", static_libs: [ "aconfig_test_java_library", "aconfig_test_java_library_exported", "aconfig_test_java_library_forcereadonly", "androidx.test.rules", "testng", ], test_suites: ["general-tests"], } java_aconfig_library { name: "aconfig_host_test_java_library", aconfig_declarations: "aconfig.test.flags", host_supported: true, mode: "test", } java_test_host { name: "AconfigJavaHostTest", srcs: [ "tests/AconfigHostTest.java", ], static_libs: [ "aconfig_host_test_java_library", "junit", ], test_suites: ["general-tests"], } // integration tests: C++ cc_aconfig_library { name: "aconfig_test_cpp_library", aconfig_declarations: "aconfig.test.flags", } cc_aconfig_library { name: "aconfig_test_cpp_library_test_variant", aconfig_declarations: "aconfig.test.flags", mode: "test", } cc_aconfig_library { name: "aconfig_test_cpp_library_force_read_only_variant", aconfig_declarations: "aconfig.test.flags", mode: "force-read-only", } cc_test { name: "aconfig.test.cpp", srcs: [ "tests/aconfig_test.cpp", ], static_libs: [ "aconfig_test_cpp_library", "libgmock", ], shared_libs: [ "server_configurable_flags", ], defaults: [ "aconfig_lib_cc_static_link.defaults", ], test_suites: ["general-tests"], } cc_test { name: "aconfig.test.cpp.test_mode", srcs: [ "tests/aconfig_test_test_variant.cpp", ], static_libs: [ "aconfig_test_cpp_library_test_variant", "libgmock", ], shared_libs: [ "server_configurable_flags", ], defaults: [ "aconfig_lib_cc_static_link.defaults", ], test_suites: ["general-tests"], } // TODO(327420679): Enable export mode for native flag library /* cc_aconfig_library { name: "aconfig_test_cpp_library_exported_variant", aconfig_declarations: "aconfig.test.flags", mode: "exported", } cc_test { name: "aconfig.test.cpp.exported_mode", srcs: [ "tests/aconfig_exported_mode_test.cpp", ], static_libs: [ "aconfig_test_cpp_library_exported_variant", "libgmock", ], shared_libs: [ "server_configurable_flags", ], defaults: [ "aconfig_lib_cc_static_link.defaults", ], test_suites: ["general-tests"], } */ cc_test { name: "aconfig.test.cpp.force_read_only_mode", srcs: [ "tests/aconfig_force_read_only_mode_test.cpp", ], static_libs: [ "aconfig_test_cpp_library_force_read_only_variant", "libgmock", ], shared_libs: [ "server_configurable_flags", ], defaults: [ "aconfig_lib_cc_static_link.defaults", ], test_suites: ["general-tests"], } rust_aconfig_library { name: "libaconfig_test_rust_library", crate_name: "aconfig_test_rust_library", aconfig_declarations: "aconfig.test.flags", host_supported: true, apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } rust_test { name: "aconfig.prod_mode.test.rust", srcs: [ "tests/aconfig_prod_mode_test.rs", ], rustlibs: [ "libaconfig_test_rust_library", ], test_suites: ["general-tests"], } rust_aconfig_library { name: "libaconfig_test_rust_library_with_test_mode", crate_name: "aconfig_test_rust_library", aconfig_declarations: "aconfig.test.flags", mode: "test", } rust_test { name: "aconfig.test_mode.test.rust", srcs: [ "tests/aconfig_test_mode_test.rs", ], rustlibs: [ "libaconfig_test_rust_library_with_test_mode", ], test_suites: ["general-tests"], } // TODO(327420679): Enable export mode for native flag library /* rust_aconfig_library { name: "libaconfig_test_rust_library_with_exported_mode", crate_name: "aconfig_test_rust_library", aconfig_declarations: "aconfig.test.flags", mode: "exported", } rust_test { name: "aconfig.exported_mode.test.rust", srcs: [ "tests/aconfig_exported_mode_test.rs", ], rustlibs: [ "libaconfig_test_rust_library_with_exported_mode", ], test_suites: ["general-tests"], } */ rust_aconfig_library { name: "libaconfig_test_rust_library_with_force_read_only_mode", crate_name: "aconfig_test_rust_library", aconfig_declarations: "aconfig.test.flags", mode: "force-read-only", } rust_test { name: "aconfig.force_read_only_mode.test.rust", srcs: [ "tests/aconfig_force_read_only_mode_test.rs", ], rustlibs: [ "libaconfig_test_rust_library_with_force_read_only_mode", ], test_suites: ["general-tests"], } ================================================ FILE: tools/aconfig/aconfig/Cargo.toml ================================================ [package] name = "aconfig" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] anyhow = "1.0.69" clap = { version = "4.1.8", features = ["derive"] } itertools = "0.10.5" protobuf = "3.2.0" serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" tinytemplate = "1.2.1" aconfig_protos = { path = "../aconfig_protos" } aconfig_storage_file = { path = "../aconfig_storage_file" } convert_finalized_flags = { path = "../convert_finalized_flags" } [build-dependencies] anyhow = "1.0.69" itertools = "0.10.5" serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" convert_finalized_flags = { path = "../convert_finalized_flags" } [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(enable_fingerprint_rust)'] } ================================================ FILE: tools/aconfig/aconfig/build.rs ================================================ use anyhow::{anyhow, Result}; use std::env; use std::fs; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; use convert_finalized_flags::read_files_to_map_using_path; use convert_finalized_flags::FinalizedFlagMap; // This fn makes assumptions about the working directory which we should not rely // on for actual (Soong) builds. It is reasonable to assume that this is being // called from the aconfig directory as cargo is used for local development and // the cargo workspace for our project is build/make/tools/aconfig. // This is meant to get the list of finalized flag // files provided by the filegroup + "locations" in soong. // Cargo-only usage is asserted via implementation of // read_files_to_map_using_env, the only public cargo-only fn. fn read_files_to_map_using_env() -> Result { let mut current_dir = std::env::current_dir()?; // Path of aconfig from the top of tree. let aconfig_path = PathBuf::from("build/make/tools/aconfig"); // Path of SDK files from the top of tree. let sdk_dir_path = PathBuf::from("prebuilts/sdk"); // Iterate up the directory structure until we have the base aconfig dir. while !current_dir.canonicalize()?.ends_with(&aconfig_path) { if let Some(parent) = current_dir.parent() { current_dir = parent.to_path_buf(); } else { return Err(anyhow!("Cannot execute outside of aconfig.")); } } // Remove the aconfig path, leaving the top of the tree. for _ in 0..aconfig_path.components().count() { current_dir.pop(); } // Get the absolute path of the sdk files. current_dir.push(sdk_dir_path); let mut flag_files = Vec::new(); // Search all sub-dirs in prebuilts/sdk for finalized-flags.txt files. // The files are in prebuilts/sdk//finalized-flags.txt. let api_level_dirs = fs::read_dir(current_dir)?; for api_level_dir in api_level_dirs { if api_level_dir.is_err() { eprintln!("Error opening directory: {}", api_level_dir.err().unwrap()); continue; } // Skip non-directories. let api_level_dir_path = api_level_dir.unwrap().path(); if !api_level_dir_path.is_dir() { continue; } // Some directories were created before trunk stable and don't have // flags, or aren't api level directories at all. let flag_file_path = api_level_dir_path.join("finalized-flags.txt"); if !flag_file_path.exists() { continue; } if let Some(path) = flag_file_path.to_str() { flag_files.push(path.to_string()); } else { eprintln!("Error converting path to string: {:?}", flag_file_path); } } read_files_to_map_using_path(flag_files) } fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("finalized_flags_record.json"); let finalized_flags_map: Result = read_files_to_map_using_env(); if finalized_flags_map.is_err() { return; } let json_str = serde_json::to_string(&finalized_flags_map.unwrap()).unwrap(); let mut f = File::create(&dest_path).unwrap(); f.write_all(json_str.as_bytes()).unwrap(); //println!("cargo:rerun-if-changed=input.txt"); } ================================================ FILE: tools/aconfig/aconfig/src/codegen/cpp.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::{ensure, Result}; use serde::Serialize; use std::collections::HashMap; use std::path::PathBuf; use tinytemplate::TinyTemplate; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag}; use crate::codegen; use crate::codegen::CodegenMode; use crate::commands::{should_include_flag, OutputFile}; pub fn generate_cpp_code( package: &str, parsed_flags_iter: I, codegen_mode: CodegenMode, flag_ids: HashMap, ) -> Result> where I: Iterator, { let mut readwrite_count = 0; let class_elements: Vec = parsed_flags_iter .map(|pf| create_class_element(package, &pf, flag_ids.clone(), &mut readwrite_count)) .collect(); let readwrite = readwrite_count > 0; let has_fixed_read_only = class_elements.iter().any(|item| item.is_fixed_read_only); let header = package.replace('.', "_"); let package_macro = header.to_uppercase(); let cpp_namespace = package.replace('.', "::"); ensure!(class_elements.len() > 0); let container = class_elements[0].container.clone(); ensure!(codegen::is_valid_name_ident(&header)); let context = Context { header: &header, package_macro: &package_macro, cpp_namespace: &cpp_namespace, package, has_fixed_read_only, readwrite, readwrite_count, is_test_mode: codegen_mode == CodegenMode::Test, class_elements, container, }; let files = [ FileSpec { name: &format!("{}.h", header), template: include_str!("../../templates/cpp_exported_header.template"), dir: "include", }, FileSpec { name: &format!("{}.cc", header), template: include_str!("../../templates/cpp_source_file.template"), dir: "", }, ]; files.iter().map(|file| generate_file(file, &context)).collect() } pub fn generate_file(file: &FileSpec, context: &Context) -> Result { let mut template = TinyTemplate::new(); template.add_template(file.name, file.template)?; let contents = template.render(file.name, &context)?; let path: PathBuf = [&file.dir, &file.name].iter().collect(); Ok(OutputFile { contents: contents.into(), path }) } #[derive(Serialize)] pub struct FileSpec<'a> { pub name: &'a str, pub template: &'a str, pub dir: &'a str, } #[derive(Serialize)] pub struct Context<'a> { pub header: &'a str, pub package_macro: &'a str, pub cpp_namespace: &'a str, pub package: &'a str, pub has_fixed_read_only: bool, pub readwrite: bool, pub readwrite_count: i32, pub is_test_mode: bool, pub class_elements: Vec, pub container: String, } #[derive(Serialize)] pub struct ClassElement { pub readwrite_idx: i32, pub readwrite: bool, pub is_fixed_read_only: bool, pub default_value: String, pub flag_name: String, pub flag_macro: String, pub flag_offset: u16, pub device_config_namespace: String, pub device_config_flag: String, pub container: String, } fn create_class_element( package: &str, pf: &ProtoParsedFlag, flag_ids: HashMap, rw_count: &mut i32, ) -> ClassElement { let no_assigned_offset = !should_include_flag(pf); let flag_offset = match flag_ids.get(pf.name()) { Some(offset) => offset, None => { // System/vendor/product RO+disabled flags have no offset in storage files. // Assign placeholder value. if no_assigned_offset { &0 } // All other flags _must_ have an offset. else { panic!("{}", format!("missing flag offset for {}", pf.name())); } } }; ClassElement { readwrite_idx: if pf.permission() == ProtoFlagPermission::READ_WRITE { let index = *rw_count; *rw_count += 1; index } else { -1 }, readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE, is_fixed_read_only: pf.is_fixed_read_only(), default_value: if pf.state() == ProtoFlagState::ENABLED { "true".to_string() } else { "false".to_string() }, flag_name: pf.name().to_string(), flag_macro: pf.name().to_uppercase(), flag_offset: *flag_offset, device_config_namespace: pf.namespace().to_string(), device_config_flag: codegen::create_device_config_ident(package, pf.name()) .expect("values checked at flag parse time"), container: pf.container().to_string(), } } #[cfg(test)] mod tests { use super::*; use aconfig_protos::ProtoParsedFlags; use std::collections::HashMap; const EXPORTED_PROD_HEADER_EXPECTED: &str = r#" #pragma once #ifndef COM_ANDROID_ACONFIG_TEST #define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG #endif #ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO #define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true #endif #ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED #define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED true #endif #ifdef __cplusplus #include namespace com::android::aconfig::test { class flag_provider_interface { public: virtual ~flag_provider_interface() = default; virtual bool disabled_ro() = 0; virtual bool disabled_rw() = 0; virtual bool disabled_rw_exported() = 0; virtual bool disabled_rw_in_other_namespace() = 0; virtual bool enabled_fixed_ro() = 0; virtual bool enabled_fixed_ro_exported() = 0; virtual bool enabled_ro() = 0; virtual bool enabled_ro_exported() = 0; virtual bool enabled_rw() = 0; }; extern std::unique_ptr provider_; inline bool disabled_ro() { return false; } inline bool disabled_rw() { return provider_->disabled_rw(); } inline bool disabled_rw_exported() { return provider_->disabled_rw_exported(); } inline bool disabled_rw_in_other_namespace() { return provider_->disabled_rw_in_other_namespace(); } constexpr inline bool enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } constexpr inline bool enabled_fixed_ro_exported() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED; } inline bool enabled_ro() { return true; } inline bool enabled_ro_exported() { return true; } inline bool enabled_rw() { return provider_->enabled_rw(); } } extern "C" { #endif // __cplusplus bool com_android_aconfig_test_disabled_ro(); bool com_android_aconfig_test_disabled_rw(); bool com_android_aconfig_test_disabled_rw_exported(); bool com_android_aconfig_test_disabled_rw_in_other_namespace(); bool com_android_aconfig_test_enabled_fixed_ro(); bool com_android_aconfig_test_enabled_fixed_ro_exported(); bool com_android_aconfig_test_enabled_ro(); bool com_android_aconfig_test_enabled_ro_exported(); bool com_android_aconfig_test_enabled_rw(); #ifdef __cplusplus } // extern "C" #endif "#; const EXPORTED_TEST_HEADER_EXPECTED: &str = r#" #pragma once #ifdef __cplusplus #include namespace com::android::aconfig::test { class flag_provider_interface { public: virtual ~flag_provider_interface() = default; virtual bool disabled_ro() = 0; virtual bool disabled_rw() = 0; virtual bool disabled_rw_exported() = 0; virtual bool disabled_rw_in_other_namespace() = 0; virtual bool enabled_fixed_ro() = 0; virtual bool enabled_fixed_ro_exported() = 0; virtual bool enabled_ro() = 0; virtual bool enabled_ro_exported() = 0; virtual bool enabled_rw() = 0; virtual void disabled_ro(bool val) = 0; virtual void disabled_rw(bool val) = 0; virtual void disabled_rw_exported(bool val) = 0; virtual void disabled_rw_in_other_namespace(bool val) = 0; virtual void enabled_fixed_ro(bool val) = 0; virtual void enabled_fixed_ro_exported(bool val) = 0; virtual void enabled_ro(bool val) = 0; virtual void enabled_ro_exported(bool val) = 0; virtual void enabled_rw(bool val) = 0; virtual void reset_flags() {} }; extern std::unique_ptr provider_; inline bool disabled_ro() { return provider_->disabled_ro(); } inline void disabled_ro(bool val) { provider_->disabled_ro(val); } inline bool disabled_rw() { return provider_->disabled_rw(); } inline void disabled_rw(bool val) { provider_->disabled_rw(val); } inline bool disabled_rw_exported() { return provider_->disabled_rw_exported(); } inline void disabled_rw_exported(bool val) { provider_->disabled_rw_exported(val); } inline bool disabled_rw_in_other_namespace() { return provider_->disabled_rw_in_other_namespace(); } inline void disabled_rw_in_other_namespace(bool val) { provider_->disabled_rw_in_other_namespace(val); } inline bool enabled_fixed_ro() { return provider_->enabled_fixed_ro(); } inline void enabled_fixed_ro(bool val) { provider_->enabled_fixed_ro(val); } inline bool enabled_fixed_ro_exported() { return provider_->enabled_fixed_ro_exported(); } inline void enabled_fixed_ro_exported(bool val) { provider_->enabled_fixed_ro_exported(val); } inline bool enabled_ro() { return provider_->enabled_ro(); } inline void enabled_ro(bool val) { provider_->enabled_ro(val); } inline bool enabled_ro_exported() { return provider_->enabled_ro_exported(); } inline void enabled_ro_exported(bool val) { provider_->enabled_ro_exported(val); } inline bool enabled_rw() { return provider_->enabled_rw(); } inline void enabled_rw(bool val) { provider_->enabled_rw(val); } inline void reset_flags() { return provider_->reset_flags(); } } extern "C" { #endif // __cplusplus bool com_android_aconfig_test_disabled_ro(); void set_com_android_aconfig_test_disabled_ro(bool val); bool com_android_aconfig_test_disabled_rw(); void set_com_android_aconfig_test_disabled_rw(bool val); bool com_android_aconfig_test_disabled_rw_exported(); void set_com_android_aconfig_test_disabled_rw_exported(bool val); bool com_android_aconfig_test_disabled_rw_in_other_namespace(); void set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val); bool com_android_aconfig_test_enabled_fixed_ro(); void set_com_android_aconfig_test_enabled_fixed_ro(bool val); bool com_android_aconfig_test_enabled_fixed_ro_exported(); void set_com_android_aconfig_test_enabled_fixed_ro_exported(bool val); bool com_android_aconfig_test_enabled_ro(); void set_com_android_aconfig_test_enabled_ro(bool val); bool com_android_aconfig_test_enabled_ro_exported(); void set_com_android_aconfig_test_enabled_ro_exported(bool val); bool com_android_aconfig_test_enabled_rw(); void set_com_android_aconfig_test_enabled_rw(bool val); void com_android_aconfig_test_reset_flags(); #ifdef __cplusplus } // extern "C" #endif "#; const EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED: &str = r#" #pragma once #ifndef COM_ANDROID_ACONFIG_TEST #define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG #endif #ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO #define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true #endif #ifdef __cplusplus #include namespace com::android::aconfig::test { class flag_provider_interface { public: virtual ~flag_provider_interface() = default; virtual bool disabled_ro() = 0; virtual bool disabled_rw() = 0; virtual bool disabled_rw_in_other_namespace() = 0; virtual bool enabled_fixed_ro() = 0; virtual bool enabled_ro() = 0; virtual bool enabled_rw() = 0; }; extern std::unique_ptr provider_; inline bool disabled_ro() { return false; } inline bool disabled_rw() { return false; } inline bool disabled_rw_in_other_namespace() { return false; } constexpr inline bool enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } inline bool enabled_ro() { return true; } inline bool enabled_rw() { return true; } } extern "C" { #endif // __cplusplus bool com_android_aconfig_test_disabled_ro(); bool com_android_aconfig_test_disabled_rw(); bool com_android_aconfig_test_disabled_rw_in_other_namespace(); bool com_android_aconfig_test_enabled_fixed_ro(); bool com_android_aconfig_test_enabled_ro(); bool com_android_aconfig_test_enabled_rw(); #ifdef __cplusplus } // extern "C" #endif "#; const PROD_SOURCE_FILE_EXPECTED: &str = r#" #include "com_android_aconfig_test.h" #include #include "aconfig_storage/aconfig_storage_read_api.hpp" #include #define LOG_TAG "aconfig_cpp_codegen" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #include namespace com::android::aconfig::test { class flag_provider : public flag_provider_interface { public: flag_provider() : cache_(4, -1) , boolean_start_index_() , flag_value_file_(nullptr) , package_exists_in_storage_(true) { auto package_map_file = aconfig_storage::get_mapped_file( "system", aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) { ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); package_exists_in_storage_ = false; return; } auto context = aconfig_storage::get_package_read_context( **package_map_file, "com.android.aconfig.test"); if (!context.ok()) { ALOGE("error: failed to get package read context: %s", context.error().c_str()); package_exists_in_storage_ = false; return; } if (!(context->package_exists)) { package_exists_in_storage_ = false; return; } // cache package boolean flag start index boolean_start_index_ = context->boolean_start_index; // unmap package map file and free memory delete *package_map_file; auto flag_value_file = aconfig_storage::get_mapped_file( "system", aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) { ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); package_exists_in_storage_ = false; return; } // cache flag value file flag_value_file_ = std::unique_ptr( *flag_value_file); } virtual bool disabled_ro() override { return false; } virtual bool disabled_rw() override { if (cache_[0] == -1) { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 0); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } cache_[0] = *value; } return cache_[0]; } virtual bool disabled_rw_exported() override { if (cache_[1] == -1) { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 1); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } cache_[1] = *value; } return cache_[1]; } virtual bool disabled_rw_in_other_namespace() override { if (cache_[2] == -1) { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 2); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } cache_[2] = *value; } return cache_[2]; } virtual bool enabled_fixed_ro() override { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } virtual bool enabled_fixed_ro_exported() override { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED; } virtual bool enabled_ro() override { return true; } virtual bool enabled_ro_exported() override { return true; } virtual bool enabled_rw() override { if (cache_[3] == -1) { if (!package_exists_in_storage_) { return true; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 7); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return true; } cache_[3] = *value; } return cache_[3]; } private: std::vector cache_ = std::vector(4, -1); uint32_t boolean_start_index_; std::unique_ptr flag_value_file_; bool package_exists_in_storage_; }; std::unique_ptr provider_ = std::make_unique(); } bool com_android_aconfig_test_disabled_ro() { return false; } bool com_android_aconfig_test_disabled_rw() { return com::android::aconfig::test::disabled_rw(); } bool com_android_aconfig_test_disabled_rw_exported() { return com::android::aconfig::test::disabled_rw_exported(); } bool com_android_aconfig_test_disabled_rw_in_other_namespace() { return com::android::aconfig::test::disabled_rw_in_other_namespace(); } bool com_android_aconfig_test_enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } bool com_android_aconfig_test_enabled_fixed_ro_exported() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO_EXPORTED; } bool com_android_aconfig_test_enabled_ro() { return true; } bool com_android_aconfig_test_enabled_ro_exported() { return true; } bool com_android_aconfig_test_enabled_rw() { return com::android::aconfig::test::enabled_rw(); } "#; const TEST_SOURCE_FILE_EXPECTED: &str = r#" #include "com_android_aconfig_test.h" #include #include "aconfig_storage/aconfig_storage_read_api.hpp" #include #define LOG_TAG "aconfig_cpp_codegen" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #include #include namespace com::android::aconfig::test { class flag_provider : public flag_provider_interface { private: std::unordered_map overrides_; uint32_t boolean_start_index_; std::unique_ptr flag_value_file_; bool package_exists_in_storage_; public: flag_provider() : overrides_() , boolean_start_index_() , flag_value_file_(nullptr) , package_exists_in_storage_(true) { auto package_map_file = aconfig_storage::get_mapped_file( "system", aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) { ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); package_exists_in_storage_ = false; return; } auto context = aconfig_storage::get_package_read_context( **package_map_file, "com.android.aconfig.test"); if (!context.ok()) { ALOGE("error: failed to get package read context: %s", context.error().c_str()); package_exists_in_storage_ = false; return; } if (!(context->package_exists)) { package_exists_in_storage_ = false; return; } // cache package boolean flag start index boolean_start_index_ = context->boolean_start_index; // unmap package map file and free memory delete *package_map_file; auto flag_value_file = aconfig_storage::get_mapped_file( "system", aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) { ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); package_exists_in_storage_ = false; return; } // cache flag value file flag_value_file_ = std::unique_ptr( *flag_value_file); } virtual bool disabled_ro() override { auto it = overrides_.find("disabled_ro"); if (it != overrides_.end()) { return it->second; } else { return false; } } virtual void disabled_ro(bool val) override { overrides_["disabled_ro"] = val; } virtual bool disabled_rw() override { auto it = overrides_.find("disabled_rw"); if (it != overrides_.end()) { return it->second; } else { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 0); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } else { return *value; } } } virtual void disabled_rw(bool val) override { overrides_["disabled_rw"] = val; } virtual bool disabled_rw_exported() override { auto it = overrides_.find("disabled_rw_exported"); if (it != overrides_.end()) { return it->second; } else { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 1); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } else { return *value; } } } virtual void disabled_rw_exported(bool val) override { overrides_["disabled_rw_exported"] = val; } virtual bool disabled_rw_in_other_namespace() override { auto it = overrides_.find("disabled_rw_in_other_namespace"); if (it != overrides_.end()) { return it->second; } else { if (!package_exists_in_storage_) { return false; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 2); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return false; } else { return *value; } } } virtual void disabled_rw_in_other_namespace(bool val) override { overrides_["disabled_rw_in_other_namespace"] = val; } virtual bool enabled_fixed_ro() override { auto it = overrides_.find("enabled_fixed_ro"); if (it != overrides_.end()) { return it->second; } else { return true; } } virtual void enabled_fixed_ro(bool val) override { overrides_["enabled_fixed_ro"] = val; } virtual bool enabled_fixed_ro_exported() override { auto it = overrides_.find("enabled_fixed_ro_exported"); if (it != overrides_.end()) { return it->second; } else { return true; } } virtual void enabled_fixed_ro_exported(bool val) override { overrides_["enabled_fixed_ro_exported"] = val; } virtual bool enabled_ro() override { auto it = overrides_.find("enabled_ro"); if (it != overrides_.end()) { return it->second; } else { return true; } } virtual void enabled_ro(bool val) override { overrides_["enabled_ro"] = val; } virtual bool enabled_ro_exported() override { auto it = overrides_.find("enabled_ro_exported"); if (it != overrides_.end()) { return it->second; } else { return true; } } virtual void enabled_ro_exported(bool val) override { overrides_["enabled_ro_exported"] = val; } virtual bool enabled_rw() override { auto it = overrides_.find("enabled_rw"); if (it != overrides_.end()) { return it->second; } else { if (!package_exists_in_storage_) { return true; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + 7); if (!value.ok()) { ALOGE("error: failed to read flag value: %s", value.error().c_str()); return true; } else { return *value; } } } virtual void enabled_rw(bool val) override { overrides_["enabled_rw"] = val; } virtual void reset_flags() override { overrides_.clear(); } }; std::unique_ptr provider_ = std::make_unique(); } bool com_android_aconfig_test_disabled_ro() { return com::android::aconfig::test::disabled_ro(); } void set_com_android_aconfig_test_disabled_ro(bool val) { com::android::aconfig::test::disabled_ro(val); } bool com_android_aconfig_test_disabled_rw() { return com::android::aconfig::test::disabled_rw(); } void set_com_android_aconfig_test_disabled_rw(bool val) { com::android::aconfig::test::disabled_rw(val); } bool com_android_aconfig_test_disabled_rw_exported() { return com::android::aconfig::test::disabled_rw_exported(); } void set_com_android_aconfig_test_disabled_rw_exported(bool val) { com::android::aconfig::test::disabled_rw_exported(val); } bool com_android_aconfig_test_disabled_rw_in_other_namespace() { return com::android::aconfig::test::disabled_rw_in_other_namespace(); } void set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val) { com::android::aconfig::test::disabled_rw_in_other_namespace(val); } bool com_android_aconfig_test_enabled_fixed_ro() { return com::android::aconfig::test::enabled_fixed_ro(); } void set_com_android_aconfig_test_enabled_fixed_ro(bool val) { com::android::aconfig::test::enabled_fixed_ro(val); } bool com_android_aconfig_test_enabled_fixed_ro_exported() { return com::android::aconfig::test::enabled_fixed_ro_exported(); } void set_com_android_aconfig_test_enabled_fixed_ro_exported(bool val) { com::android::aconfig::test::enabled_fixed_ro_exported(val); } bool com_android_aconfig_test_enabled_ro() { return com::android::aconfig::test::enabled_ro(); } void set_com_android_aconfig_test_enabled_ro(bool val) { com::android::aconfig::test::enabled_ro(val); } bool com_android_aconfig_test_enabled_ro_exported() { return com::android::aconfig::test::enabled_ro_exported(); } void set_com_android_aconfig_test_enabled_ro_exported(bool val) { com::android::aconfig::test::enabled_ro_exported(val); } bool com_android_aconfig_test_enabled_rw() { return com::android::aconfig::test::enabled_rw(); } void set_com_android_aconfig_test_enabled_rw(bool val) { com::android::aconfig::test::enabled_rw(val); } void com_android_aconfig_test_reset_flags() { com::android::aconfig::test::reset_flags(); } "#; const FORCE_READ_ONLY_SOURCE_FILE_EXPECTED: &str = r#" #include "com_android_aconfig_test.h" namespace com::android::aconfig::test { class flag_provider : public flag_provider_interface { public: virtual bool disabled_ro() override { return false; } virtual bool disabled_rw() override { return false; } virtual bool disabled_rw_in_other_namespace() override { return false; } virtual bool enabled_fixed_ro() override { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } virtual bool enabled_ro() override { return true; } virtual bool enabled_rw() override { return true; } }; std::unique_ptr provider_ = std::make_unique(); } bool com_android_aconfig_test_disabled_ro() { return false; } bool com_android_aconfig_test_disabled_rw() { return false; } bool com_android_aconfig_test_disabled_rw_in_other_namespace() { return false; } bool com_android_aconfig_test_enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } bool com_android_aconfig_test_enabled_ro() { return true; } bool com_android_aconfig_test_enabled_rw() { return true; } "#; const READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED: &str = r#" #pragma once #ifndef COM_ANDROID_ACONFIG_TEST #define COM_ANDROID_ACONFIG_TEST(FLAG) COM_ANDROID_ACONFIG_TEST_##FLAG #endif #ifndef COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO #define COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO false #endif #ifndef COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO #define COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO true #endif #ifdef __cplusplus #include namespace com::android::aconfig::test { class flag_provider_interface { public: virtual ~flag_provider_interface() = default; virtual bool disabled_fixed_ro() = 0; virtual bool disabled_ro() = 0; virtual bool enabled_fixed_ro() = 0; virtual bool enabled_ro() = 0; }; extern std::unique_ptr provider_; constexpr inline bool disabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO; } inline bool disabled_ro() { return false; } constexpr inline bool enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } inline bool enabled_ro() { return true; } } extern "C" { #endif // __cplusplus bool com_android_aconfig_test_disabled_fixed_ro(); bool com_android_aconfig_test_disabled_ro(); bool com_android_aconfig_test_enabled_fixed_ro(); bool com_android_aconfig_test_enabled_ro(); #ifdef __cplusplus } // extern "C" #endif "#; const READ_ONLY_PROD_SOURCE_FILE_EXPECTED: &str = r#" #include "com_android_aconfig_test.h" namespace com::android::aconfig::test { class flag_provider : public flag_provider_interface { public: virtual bool disabled_fixed_ro() override { return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO; } virtual bool disabled_ro() override { return false; } virtual bool enabled_fixed_ro() override { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } virtual bool enabled_ro() override { return true; } }; std::unique_ptr provider_ = std::make_unique(); } bool com_android_aconfig_test_disabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_DISABLED_FIXED_RO; } bool com_android_aconfig_test_disabled_ro() { return false; } bool com_android_aconfig_test_enabled_fixed_ro() { return COM_ANDROID_ACONFIG_TEST_ENABLED_FIXED_RO; } bool com_android_aconfig_test_enabled_ro() { return true; } "#; use crate::commands::assign_flag_ids; fn test_generate_cpp_code( parsed_flags: ProtoParsedFlags, mode: CodegenMode, expected_header: &str, expected_src: &str, ) { let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let generated = generate_cpp_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode, flag_ids, ) .unwrap(); let mut generated_files_map = HashMap::new(); for file in generated { generated_files_map.insert( String::from(file.path.to_str().unwrap()), String::from_utf8(file.contents).unwrap(), ); } let mut target_file_path = String::from("include/com_android_aconfig_test.h"); assert!(generated_files_map.contains_key(&target_file_path)); assert_eq!( None, crate::test::first_significant_code_diff( expected_header, generated_files_map.get(&target_file_path).unwrap() ) ); target_file_path = String::from("com_android_aconfig_test.cc"); assert!(generated_files_map.contains_key(&target_file_path)); assert_eq!( None, crate::test::first_significant_code_diff( expected_src, generated_files_map.get(&target_file_path).unwrap() ) ); } #[test] fn test_generate_cpp_code_for_prod() { let parsed_flags = crate::test::parse_test_flags(); test_generate_cpp_code( parsed_flags, CodegenMode::Production, EXPORTED_PROD_HEADER_EXPECTED, PROD_SOURCE_FILE_EXPECTED, ); } #[test] fn test_generate_cpp_code_for_test() { let parsed_flags = crate::test::parse_test_flags(); test_generate_cpp_code( parsed_flags, CodegenMode::Test, EXPORTED_TEST_HEADER_EXPECTED, TEST_SOURCE_FILE_EXPECTED, ); } #[test] fn test_generate_cpp_code_for_force_read_only() { let parsed_flags = crate::test::parse_test_flags(); test_generate_cpp_code( parsed_flags, CodegenMode::ForceReadOnly, EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED, FORCE_READ_ONLY_SOURCE_FILE_EXPECTED, ); } #[test] fn test_generate_cpp_code_for_read_only_prod() { let parsed_flags = crate::test::parse_read_only_test_flags(); test_generate_cpp_code( parsed_flags, CodegenMode::Production, READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED, READ_ONLY_PROD_SOURCE_FILE_EXPECTED, ); } } ================================================ FILE: tools/aconfig/aconfig/src/codegen/java.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::Result; use serde::Serialize; use std::collections::{BTreeMap, BTreeSet}; use std::path::PathBuf; use tinytemplate::TinyTemplate; use crate::codegen; use crate::codegen::CodegenMode; use crate::commands::{should_include_flag, OutputFile}; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag}; use convert_finalized_flags::{FinalizedFlag, FinalizedFlagMap}; use std::collections::HashMap; // Arguments to configure codegen for generate_java_code. pub struct JavaCodegenConfig { pub codegen_mode: CodegenMode, pub flag_ids: HashMap, pub allow_instrumentation: bool, pub package_fingerprint: u64, pub new_exported: bool, pub single_exported_file: bool, pub finalized_flags: FinalizedFlagMap, } pub fn generate_java_code( package: &str, parsed_flags_iter: I, config: JavaCodegenConfig, ) -> Result> where I: Iterator, { let flag_elements: Vec = parsed_flags_iter .map(|pf| { create_flag_element(package, &pf, config.flag_ids.clone(), &config.finalized_flags) }) .collect(); let namespace_flags = gen_flags_by_namespace(&flag_elements); let properties_set: BTreeSet = flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect(); let is_test_mode = config.codegen_mode == CodegenMode::Test; let library_exported = config.codegen_mode == CodegenMode::Exported; let runtime_lookup_required = flag_elements.iter().any(|elem| elem.is_read_write) || library_exported; let container = (flag_elements.first().expect("zero template flags").container).to_string(); let is_platform_container = matches!(container.as_str(), "system" | "system_ext" | "product" | "vendor"); let context = Context { flag_elements, namespace_flags, is_test_mode, runtime_lookup_required, properties_set, package_name: package.to_string(), library_exported, allow_instrumentation: config.allow_instrumentation, container, is_platform_container, package_fingerprint: format!("0x{:X}L", config.package_fingerprint), new_exported: config.new_exported, single_exported_file: config.single_exported_file, }; let mut template = TinyTemplate::new(); if library_exported && config.single_exported_file { template.add_template( "ExportedFlags.java", include_str!("../../templates/ExportedFlags.java.template"), )?; } template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?; add_feature_flags_impl_template(&context, &mut template)?; template.add_template( "FeatureFlags.java", include_str!("../../templates/FeatureFlags.java.template"), )?; template.add_template( "CustomFeatureFlags.java", include_str!("../../templates/CustomFeatureFlags.java.template"), )?; template.add_template( "FakeFeatureFlagsImpl.java", include_str!("../../templates/FakeFeatureFlagsImpl.java.template"), )?; let path: PathBuf = package.split('.').collect(); let mut files = vec![ "Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "CustomFeatureFlags.java", "FakeFeatureFlagsImpl.java", ]; if library_exported && config.single_exported_file { files.push("ExportedFlags.java"); } files .iter() .map(|file| { Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file), }) }) .collect::>>() } fn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec { let mut namespace_to_flag: BTreeMap> = BTreeMap::new(); for flag in flags { match namespace_to_flag.get_mut(&flag.device_config_namespace) { Some(flag_list) => flag_list.push(flag.clone()), None => { namespace_to_flag.insert(flag.device_config_namespace.clone(), vec![flag.clone()]); } } } namespace_to_flag .iter() .map(|(namespace, flags)| NamespaceFlags { namespace: namespace.to_string(), flags: flags.clone(), }) .collect() } #[derive(Serialize)] struct Context { pub flag_elements: Vec, pub namespace_flags: Vec, pub is_test_mode: bool, pub runtime_lookup_required: bool, pub properties_set: BTreeSet, pub package_name: String, pub library_exported: bool, pub allow_instrumentation: bool, pub container: String, pub is_platform_container: bool, pub package_fingerprint: String, pub new_exported: bool, pub single_exported_file: bool, } #[derive(Serialize, Debug)] struct NamespaceFlags { pub namespace: String, pub flags: Vec, } #[derive(Serialize, Clone, Debug)] struct FlagElement { pub container: String, pub default_value: bool, pub device_config_namespace: String, pub device_config_flag: String, pub flag_name: String, pub flag_name_constant_suffix: String, pub flag_offset: u16, pub is_read_write: bool, pub method_name: String, pub properties: String, pub finalized_sdk_present: bool, pub finalized_sdk_value: i32, } fn create_flag_element( package: &str, pf: &ProtoParsedFlag, flag_offsets: HashMap, finalized_flags: &FinalizedFlagMap, ) -> FlagElement { let device_config_flag = codegen::create_device_config_ident(package, pf.name()) .expect("values checked at flag parse time"); let no_assigned_offset = !should_include_flag(pf); let flag_offset = match flag_offsets.get(pf.name()) { Some(offset) => offset, None => { // System/vendor/product RO+disabled flags have no offset in storage files. // Assign placeholder value. if no_assigned_offset { &0 } // All other flags _must_ have an offset. else { panic!("{}", format!("missing flag offset for {}", pf.name())); } } }; // An empty map is provided if check_api_level is disabled. let mut finalized_sdk_present: bool = false; let mut finalized_sdk_value: i32 = 0; if !finalized_flags.is_empty() { let finalized_sdk = finalized_flags.get_finalized_level(&FinalizedFlag { flag_name: pf.name().to_string(), package_name: package.to_string(), }); finalized_sdk_present = finalized_sdk.is_some(); finalized_sdk_value = finalized_sdk.map(|f| f.0).unwrap_or_default(); } FlagElement { container: pf.container().to_string(), default_value: pf.state() == ProtoFlagState::ENABLED, device_config_namespace: pf.namespace().to_string(), device_config_flag, flag_name: pf.name().to_string(), flag_name_constant_suffix: pf.name().to_ascii_uppercase(), flag_offset: *flag_offset, is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE, method_name: format_java_method_name(pf.name()), properties: format_property_name(pf.namespace()), finalized_sdk_present, finalized_sdk_value, } } fn format_java_method_name(flag_name: &str) -> String { let splits: Vec<&str> = flag_name.split('_').filter(|&word| !word.is_empty()).collect(); if splits.len() == 1 { let name = splits[0]; name[0..1].to_ascii_lowercase() + &name[1..] } else { splits .iter() .enumerate() .map(|(index, word)| { if index == 0 { word.to_ascii_lowercase() } else { word[0..1].to_ascii_uppercase() + &word[1..].to_ascii_lowercase() } }) .collect::>() .join("") } } fn format_property_name(property_name: &str) -> String { let name = format_java_method_name(property_name); format!("mProperties{}{}", &name[0..1].to_ascii_uppercase(), &name[1..]) } fn add_feature_flags_impl_template( context: &Context, template: &mut TinyTemplate, ) -> Result<(), tinytemplate::error::Error> { if context.is_test_mode { // Test mode has its own template, so use regardless of any other settings. template.add_template( "FeatureFlagsImpl.java", include_str!("../../templates/FeatureFlagsImpl.test_mode.java.template"), )?; return Ok(()); } match (context.library_exported, context.new_exported, context.allow_instrumentation) { // Exported library with new_exported enabled, use new storage exported template. (true, true, _) => { template.add_template( "FeatureFlagsImpl.java", include_str!("../../templates/FeatureFlagsImpl.exported.java.template"), )?; } // Exported library with new_exported NOT enabled, use legacy (device // config) template, because regardless of allow_instrumentation, we use // device config for exported libs if new_exported isn't enabled. // Remove once new_exported is fully rolled out. (true, false, _) => { template.add_template( "FeatureFlagsImpl.java", include_str!("../../templates/FeatureFlagsImpl.deviceConfig.java.template"), )?; } // New storage internal mode. (false, _, true) => { template.add_template( "FeatureFlagsImpl.java", include_str!("../../templates/FeatureFlagsImpl.new_storage.java.template"), )?; } // Device config internal mode. Use legacy (device config) template. (false, _, false) => { template.add_template( "FeatureFlagsImpl.java", include_str!("../../templates/FeatureFlagsImpl.deviceConfig.java.template"), )?; } }; Ok(()) } #[cfg(test)] mod tests { use convert_finalized_flags::ApiLevel; use super::*; use crate::commands::assign_flag_ids; use std::collections::HashMap; const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public interface FeatureFlags { @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRo(); @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRw(); @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRwExported(); @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRwInOtherNamespace(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledFixedRo(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledFixedRoExported(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledRo(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledRoExported(); @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledRw(); } "#; const EXPECTED_FLAG_COMMON_CONTENT: &str = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public final class Flags { /** @hide */ public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro"; /** @hide */ public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw"; /** @hide */ public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported"; /** @hide */ public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = "com.android.aconfig.test.disabled_rw_in_other_namespace"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported"; /** @hide */ public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro"; /** @hide */ public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported"; /** @hide */ public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw"; @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRo() { return FEATURE_FLAGS.disabledRo(); } @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRw() { return FEATURE_FLAGS.disabledRw(); } @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRwExported() { return FEATURE_FLAGS.disabledRwExported(); } @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRwInOtherNamespace() { return FEATURE_FLAGS.disabledRwInOtherNamespace(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledFixedRo() { return FEATURE_FLAGS.enabledFixedRo(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledFixedRoExported() { return FEATURE_FLAGS.enabledFixedRoExported(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledRo() { return FEATURE_FLAGS.enabledRo(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledRoExported() { return FEATURE_FLAGS.enabledRoExported(); } @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledRw() { return FEATURE_FLAGS.enabledRw(); } "#; const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; /** @hide */ public class CustomFeatureFlags implements FeatureFlags { private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) { mGetValueImpl = getValueImpl; } @Override @UnsupportedAppUsage public boolean disabledRo() { return getValue(Flags.FLAG_DISABLED_RO, FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { return getValue(Flags.FLAG_DISABLED_RW, FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwExported() { return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, FeatureFlags::disabledRwExported); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { return getValue(Flags.FLAG_ENABLED_FIXED_RO, FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledFixedRoExported() { return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, FeatureFlags::enabledFixedRoExported); } @Override @UnsupportedAppUsage public boolean enabledRo() { return getValue(Flags.FLAG_ENABLED_RO, FeatureFlags::enabledRo); } @Override @UnsupportedAppUsage public boolean enabledRoExported() { return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, FeatureFlags::enabledRoExported); } @Override @UnsupportedAppUsage public boolean enabledRw() { return getValue(Flags.FLAG_ENABLED_RW, FeatureFlags::enabledRw); } public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { return true; } return false; } @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } protected boolean getValue(String flagName, Predicate getter) { return mGetValueImpl.test(flagName, getter); } public List getFlagNames() { return Arrays.asList( Flags.FLAG_DISABLED_RO, Flags.FLAG_DISABLED_RW, Flags.FLAG_DISABLED_RW_EXPORTED, Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, Flags.FLAG_ENABLED_FIXED_RO, Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, Flags.FLAG_ENABLED_RO, Flags.FLAG_ENABLED_RO_EXPORTED, Flags.FLAG_ENABLED_RW ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, Flags.FLAG_ENABLED_FIXED_RO, Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, Flags.FLAG_ENABLED_RO, Flags.FLAG_ENABLED_RO_EXPORTED, "" ) ); } "#; const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#" package com.android.aconfig.test; import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; /** @hide */ public class FakeFeatureFlagsImpl extends CustomFeatureFlags { private final Map mFlagMap = new HashMap<>(); private final FeatureFlags mDefaults; public FakeFeatureFlagsImpl() { this(null); } public FakeFeatureFlagsImpl(FeatureFlags defaults) { super(null); mDefaults = defaults; // Initialize the map with null values for (String flagName : getFlagNames()) { mFlagMap.put(flagName, null); } } @Override protected boolean getValue(String flagName, Predicate getter) { Boolean value = this.mFlagMap.get(flagName); if (value != null) { return value; } if (mDefaults != null) { return getter.test(mDefaults); } throw new IllegalArgumentException(flagName + " is not set"); } public void setFlag(String flagName, boolean value) { if (!this.mFlagMap.containsKey(flagName)) { throw new IllegalArgumentException("no such flag " + flagName); } this.mFlagMap.put(flagName, value); } public void resetAll() { for (Map.Entry entry : mFlagMap.entrySet()) { entry.setValue(null); } } } "#; #[test] fn test_generate_java_code_production() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Production; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: false, single_exported_file: false, finalized_flags: FinalizedFlagMap::new(), }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; let expected_featureflagsmpl_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; import android.os.flagging.PlatformAconfigPackageInternal; import android.util.Log; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { private static final String TAG = "FeatureFlagsImpl"; private static volatile boolean isCached = false; private static boolean disabledRw = false; private static boolean disabledRwExported = false; private static boolean disabledRwInOtherNamespace = false; private static boolean enabledRw = true; private void init() { try { PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load("com.android.aconfig.test", 0x5081CE7221C77064L); disabledRw = reader.getBooleanFlagValue(0); disabledRwExported = reader.getBooleanFlagValue(1); enabledRw = reader.getBooleanFlagValue(7); disabledRwInOtherNamespace = reader.getBooleanFlagValue(2); } catch (Exception e) { Log.e(TAG, e.toString()); } catch (LinkageError e) { // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.e(TAG, e.toString()); } isCached = true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRo() { return false; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRw() { if (!isCached) { init(); } return disabledRw; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRwExported() { if (!isCached) { init(); } return disabledRwExported; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { if (!isCached) { init(); } return disabledRwInOtherNamespace; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledFixedRo() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledFixedRoExported() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledRo() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledRoExported() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledRw() { if (!isCached) { init(); } return enabledRw; } } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsmpl_content), ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), ( "com/android/aconfig/test/CustomFeatureFlags.java", EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } #[test] fn test_generate_java_code_exported() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Exported; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: false, single_exported_file: false, finalized_flags: FinalizedFlagMap::new(), }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = r#" package com.android.aconfig.test; import android.os.Build; /** @hide */ public final class Flags { /** @hide */ public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported"; /** @hide */ public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported"; public static boolean disabledRwExported() { return FEATURE_FLAGS.disabledRwExported(); } public static boolean enabledFixedRoExported() { return FEATURE_FLAGS.enabledFixedRoExported(); } public static boolean enabledRoExported() { return FEATURE_FLAGS.enabledRoExported(); } private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); } "#; let expect_feature_flags_content = r#" package com.android.aconfig.test; /** @hide */ public interface FeatureFlags { boolean disabledRwExported(); boolean enabledFixedRoExported(); boolean enabledRoExported(); } "#; let expect_feature_flags_impl_content = r#" package com.android.aconfig.test; import android.os.Binder; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { private static volatile boolean aconfig_test_is_cached = false; private static boolean disabledRwExported = false; private static boolean enabledFixedRoExported = false; private static boolean enabledRoExported = false; private void load_overrides_aconfig_test() { final long ident = Binder.clearCallingIdentity(); try { Properties properties = DeviceConfig.getProperties("aconfig_test"); disabledRwExported = properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false); enabledFixedRoExported = properties.getBoolean(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false); enabledRoExported = properties.getBoolean(Flags.FLAG_ENABLED_RO_EXPORTED, false); } catch (NullPointerException e) { throw new RuntimeException( "Cannot read value from namespace aconfig_test " + "from DeviceConfig. It could be that the code using flag " + "executed before SettingsProvider initialization. Please use " + "fixed read-only flag by adding is_fixed_read_only: true in " + "flag declaration.", e ); } catch (SecurityException e) { // for isolated process case, skip loading flag value from the storage, use the default } finally { Binder.restoreCallingIdentity(ident); } aconfig_test_is_cached = true; } @Override public boolean disabledRwExported() { if (!aconfig_test_is_cached) { load_overrides_aconfig_test(); } return disabledRwExported; } @Override public boolean enabledFixedRoExported() { if (!aconfig_test_is_cached) { load_overrides_aconfig_test(); } return enabledFixedRoExported; } @Override public boolean enabledRoExported() { if (!aconfig_test_is_cached) { load_overrides_aconfig_test(); } return enabledRoExported; } }"#; let expect_custom_feature_flags_content = r#" package com.android.aconfig.test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; import android.os.Build; /** @hide */ public class CustomFeatureFlags implements FeatureFlags { private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) { mGetValueImpl = getValueImpl; } @Override public boolean disabledRwExported() { return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, FeatureFlags::disabledRwExported); } @Override public boolean enabledFixedRoExported() { return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, FeatureFlags::enabledFixedRoExported); } @Override public boolean enabledRoExported() { return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, FeatureFlags::enabledRoExported); } protected boolean getValue(String flagName, Predicate getter) { return mGetValueImpl.test(flagName, getter); } public List getFlagNames() { return Arrays.asList( Flags.FLAG_DISABLED_RW_EXPORTED, Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, Flags.FLAG_ENABLED_RO_EXPORTED ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( "" ) ); private Map mFinalizedFlags = new HashMap<>( Map.ofEntries( Map.entry("", Integer.MAX_VALUE) ) ); public boolean isFlagFinalized(String flagName) { if (!mFinalizedFlags.containsKey(flagName)) { return false; } return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName); } } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content), ( "com/android/aconfig/test/CustomFeatureFlags.java", expect_custom_feature_flags_content, ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } #[test] fn test_generate_java_code_new_exported() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Exported; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: true, single_exported_file: false, finalized_flags: FinalizedFlagMap::new(), }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = r#" package com.android.aconfig.test; import android.os.Build; /** @hide */ public final class Flags { /** @hide */ public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported"; /** @hide */ public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported"; public static boolean disabledRwExported() { return FEATURE_FLAGS.disabledRwExported(); } public static boolean enabledFixedRoExported() { return FEATURE_FLAGS.enabledFixedRoExported(); } public static boolean enabledRoExported() { return FEATURE_FLAGS.enabledRoExported(); } private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); } "#; let expect_feature_flags_content = r#" package com.android.aconfig.test; /** @hide */ public interface FeatureFlags { boolean disabledRwExported(); boolean enabledFixedRoExported(); boolean enabledRoExported(); } "#; let expect_feature_flags_impl_content = r#" package com.android.aconfig.test; import android.os.Build; import android.os.flagging.AconfigPackage; import android.util.Log; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { private static final String TAG = "FeatureFlagsImplExport"; private static volatile boolean isCached = false; private static boolean disabledRwExported = false; private static boolean enabledFixedRoExported = false; private static boolean enabledRoExported = false; private void init() { try { AconfigPackage reader = AconfigPackage.load("com.android.aconfig.test"); disabledRwExported = reader.getBooleanFlagValue("disabled_rw_exported", false); enabledFixedRoExported = reader.getBooleanFlagValue("enabled_fixed_ro_exported", false); enabledRoExported = reader.getBooleanFlagValue("enabled_ro_exported", false); } catch (Exception e) { // pass Log.e(TAG, e.toString()); } catch (LinkageError e) { // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.w(TAG, e.toString()); } isCached = true; } @Override public boolean disabledRwExported() { if (!isCached) { init(); } return disabledRwExported; } @Override public boolean enabledFixedRoExported() { if (!isCached) { init(); } return enabledFixedRoExported; } @Override public boolean enabledRoExported() { if (!isCached) { init(); } return enabledRoExported; } }"#; let expect_custom_feature_flags_content = r#" package com.android.aconfig.test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; import android.os.Build; /** @hide */ public class CustomFeatureFlags implements FeatureFlags { private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) { mGetValueImpl = getValueImpl; } @Override public boolean disabledRwExported() { return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, FeatureFlags::disabledRwExported); } @Override public boolean enabledFixedRoExported() { return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, FeatureFlags::enabledFixedRoExported); } @Override public boolean enabledRoExported() { return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, FeatureFlags::enabledRoExported); } protected boolean getValue(String flagName, Predicate getter) { return mGetValueImpl.test(flagName, getter); } public List getFlagNames() { return Arrays.asList( Flags.FLAG_DISABLED_RW_EXPORTED, Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, Flags.FLAG_ENABLED_RO_EXPORTED ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( "" ) ); private Map mFinalizedFlags = new HashMap<>( Map.ofEntries( Map.entry("", Integer.MAX_VALUE) ) ); public boolean isFlagFinalized(String flagName) { if (!mFinalizedFlags.containsKey(flagName)) { return false; } return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName); } } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content), ( "com/android/aconfig/test/CustomFeatureFlags.java", expect_custom_feature_flags_content, ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } #[test] fn test_generate_java_code_new_exported_with_sdk_check() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Exported; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let mut finalized_flags = FinalizedFlagMap::new(); finalized_flags.insert_if_new( ApiLevel(36), FinalizedFlag { flag_name: "disabled_rw_exported".to_string(), package_name: "com.android.aconfig.test".to_string(), }, ); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: true, single_exported_file: false, finalized_flags, }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = r#" package com.android.aconfig.test; import android.os.Build; /** @hide */ public final class Flags { /** @hide */ public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported"; /** @hide */ public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported"; public static boolean disabledRwExported() { if (Build.VERSION.SDK_INT >= 36) { return true; } return FEATURE_FLAGS.disabledRwExported(); } public static boolean enabledFixedRoExported() { return FEATURE_FLAGS.enabledFixedRoExported(); } public static boolean enabledRoExported() { return FEATURE_FLAGS.enabledRoExported(); } private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); } "#; let expect_feature_flags_content = r#" package com.android.aconfig.test; /** @hide */ public interface FeatureFlags { boolean disabledRwExported(); boolean enabledFixedRoExported(); boolean enabledRoExported(); } "#; let expect_feature_flags_impl_content = r#" package com.android.aconfig.test; import android.os.Build; import android.os.flagging.AconfigPackage; import android.util.Log; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { private static final String TAG = "FeatureFlagsImplExport"; private static volatile boolean isCached = false; private static boolean disabledRwExported = false; private static boolean enabledFixedRoExported = false; private static boolean enabledRoExported = false; private void init() { try { AconfigPackage reader = AconfigPackage.load("com.android.aconfig.test"); disabledRwExported = Build.VERSION.SDK_INT >= 36 ? true : reader.getBooleanFlagValue("disabled_rw_exported", false); enabledFixedRoExported = reader.getBooleanFlagValue("enabled_fixed_ro_exported", false); enabledRoExported = reader.getBooleanFlagValue("enabled_ro_exported", false); } catch (Exception e) { // pass Log.e(TAG, e.toString()); } catch (LinkageError e) { // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.w(TAG, e.toString()); } isCached = true; } @Override public boolean disabledRwExported() { if (!isCached) { init(); } return disabledRwExported; } @Override public boolean enabledFixedRoExported() { if (!isCached) { init(); } return enabledFixedRoExported; } @Override public boolean enabledRoExported() { if (!isCached) { init(); } return enabledRoExported; } }"#; let expect_custom_feature_flags_content = r#" package com.android.aconfig.test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; import android.os.Build; /** @hide */ public class CustomFeatureFlags implements FeatureFlags { private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) { mGetValueImpl = getValueImpl; } @Override public boolean disabledRwExported() { return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, FeatureFlags::disabledRwExported); } @Override public boolean enabledFixedRoExported() { return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, FeatureFlags::enabledFixedRoExported); } @Override public boolean enabledRoExported() { return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, FeatureFlags::enabledRoExported); } protected boolean getValue(String flagName, Predicate getter) { return mGetValueImpl.test(flagName, getter); } public List getFlagNames() { return Arrays.asList( Flags.FLAG_DISABLED_RW_EXPORTED, Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, Flags.FLAG_ENABLED_RO_EXPORTED ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( "" ) ); private Map mFinalizedFlags = new HashMap<>( Map.ofEntries( Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, 36), Map.entry("", Integer.MAX_VALUE) ) ); public boolean isFlagFinalized(String flagName) { if (!mFinalizedFlags.containsKey(flagName)) { return false; } return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName); } } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content), ( "com/android/aconfig/test/CustomFeatureFlags.java", expect_custom_feature_flags_content, ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } // Test that the SDK check isn't added unless the library is exported (even // if the flag is present in finalized_flags). #[test] fn test_generate_java_code_flags_with_sdk_check() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Production; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let mut finalized_flags = FinalizedFlagMap::new(); finalized_flags.insert_if_new( ApiLevel(36), FinalizedFlag { flag_name: "disabled_rw".to_string(), package_name: "com.android.aconfig.test".to_string(), }, ); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: true, single_exported_file: false, finalized_flags, }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; let file = generated_files.iter().find(|f| f.path.ends_with("Flags.java")).unwrap(); assert_eq!( None, crate::test::first_significant_code_diff( &expect_flags_content, &String::from_utf8(file.contents.clone()).unwrap() ), "Flags content is not correct" ); } #[test] fn test_generate_java_code_test() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Test; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: false, single_exported_file: false, finalized_flags: FinalizedFlagMap::new(), }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" public static void setFeatureFlags(FeatureFlags featureFlags) { Flags.FEATURE_FLAGS = featureFlags; } public static void unsetFeatureFlags() { Flags.FEATURE_FLAGS = null; } private static FeatureFlags FEATURE_FLAGS; } "#; let expect_featureflagsimpl_content = r#" package com.android.aconfig.test; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean disabledRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean disabledRw() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean disabledRwExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean disabledRwInOtherNamespace() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean enabledFixedRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean enabledFixedRoExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean enabledRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean enabledRoExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor public boolean enabledRw() { throw new UnsupportedOperationException( "Method is not implemented."); } } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ( "com/android/aconfig/test/CustomFeatureFlags.java", EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } #[test] fn test_generate_java_code_force_read_only() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::ForceReadOnly; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: false, single_exported_file: false, finalized_flags: FinalizedFlagMap::new(), }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_featureflags_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public interface FeatureFlags { @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRo(); @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRw(); @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean disabledRwInOtherNamespace(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledFixedRo(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledRo(); @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage boolean enabledRw(); }"#; let expect_featureflagsimpl_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRo() { return false; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRw() { return false; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { return false; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledFixedRo() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledRo() { return true; } @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean enabledRw() { return true; } } "#; let expect_flags_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public final class Flags { /** @hide */ public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro"; /** @hide */ public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw"; /** @hide */ public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = "com.android.aconfig.test.disabled_rw_in_other_namespace"; /** @hide */ public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro"; /** @hide */ public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro"; /** @hide */ public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw"; @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRo() { return FEATURE_FLAGS.disabledRo(); } @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRw() { return FEATURE_FLAGS.disabledRw(); } @com.android.aconfig.annotations.AssumeFalseForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean disabledRwInOtherNamespace() { return FEATURE_FLAGS.disabledRwInOtherNamespace(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledFixedRo() { return FEATURE_FLAGS.enabledFixedRo(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledRo() { return FEATURE_FLAGS.enabledRo(); } @com.android.aconfig.annotations.AssumeTrueForR8 @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public static boolean enabledRw() { return FEATURE_FLAGS.enabledRw(); } private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; let expect_customfeatureflags_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; /** @hide */ public class CustomFeatureFlags implements FeatureFlags { private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) { mGetValueImpl = getValueImpl; } @Override @UnsupportedAppUsage public boolean disabledRo() { return getValue(Flags.FLAG_DISABLED_RO, FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { return getValue(Flags.FLAG_DISABLED_RW, FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { return getValue(Flags.FLAG_ENABLED_FIXED_RO, FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledRo() { return getValue(Flags.FLAG_ENABLED_RO, FeatureFlags::enabledRo); } @Override @UnsupportedAppUsage public boolean enabledRw() { return getValue(Flags.FLAG_ENABLED_RW, FeatureFlags::enabledRw); } public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { return true; } return false; } @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } protected boolean getValue(String flagName, Predicate getter) { return mGetValueImpl.test(flagName, getter); } public List getFlagNames() { return Arrays.asList( Flags.FLAG_DISABLED_RO, Flags.FLAG_DISABLED_RW, Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, Flags.FLAG_ENABLED_FIXED_RO, Flags.FLAG_ENABLED_RO, Flags.FLAG_ENABLED_RW ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, Flags.FLAG_DISABLED_RW, Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, Flags.FLAG_ENABLED_FIXED_RO, Flags.FLAG_ENABLED_RO, Flags.FLAG_ENABLED_RW, "" ) ); } "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content), ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); for file in generated_files { let file_path = file.path.to_str().unwrap(); assert!(file_set.contains_key(file_path), "Cannot find {}", file_path); assert_eq!( None, crate::test::first_significant_code_diff( file_set.get(file_path).unwrap(), &String::from_utf8(file.contents).unwrap() ), "File {} content is not correct", file_path ); file_set.remove(file_path); } assert!(file_set.is_empty()); } #[test] fn test_generate_java_code_exported_flags() { let parsed_flags = crate::test::parse_test_flags(); let mode = CodegenMode::Exported; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let mut finalized_flags = FinalizedFlagMap::new(); finalized_flags.insert_if_new( ApiLevel(36), FinalizedFlag { flag_name: "disabled_rw_exported".to_string(), package_name: "com.android.aconfig.test".to_string(), }, ); let config = JavaCodegenConfig { codegen_mode: mode, flag_ids, allow_instrumentation: true, package_fingerprint: 5801144784618221668, new_exported: true, single_exported_file: true, finalized_flags, }; let generated_files = generate_java_code( crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), config, ) .unwrap(); let expect_exported_flags_content = r#" package com.android.aconfig.test; import android.os.Build; import android.os.flagging.AconfigPackage; import android.util.Log; public final class ExportedFlags { public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported"; public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported"; public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported"; private static final String TAG = "ExportedFlags"; private static volatile boolean isCached = false; private static boolean disabledRwExported = false; private static boolean enabledFixedRoExported = false; private static boolean enabledRoExported = false; private ExportedFlags() {} private void init() { try { AconfigPackage reader = AconfigPackage.load("com.android.aconfig.test"); disabledRwExported = reader.getBooleanFlagValue("disabled_rw_exported", false); enabledFixedRoExported = reader.getBooleanFlagValue("enabled_fixed_ro_exported", false); enabledRoExported = reader.getBooleanFlagValue("enabled_ro_exported", false); } catch (Exception e) { // pass Log.e(TAG, e.toString()); } catch (LinkageError e) { // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.w(TAG, e.toString()); } isCached = true; } public static boolean disabledRwExported() { if (Build.VERSION.SDK_INT >= 36) { return true; } if (!featureFlags.isCached) { featureFlags.init(); } return featureFlags.disabledRwExported; } public static boolean enabledFixedRoExported() { if (!featureFlags.isCached) { featureFlags.init(); } return featureFlags.enabledFixedRoExported; } public static boolean enabledRoExported() { if (!featureFlags.isCached) { featureFlags.init(); } return featureFlags.enabledRoExported; } private static ExportedFlags featureFlags = new ExportedFlags(); }"#; let file = generated_files.iter().find(|f| f.path.ends_with("ExportedFlags.java")).unwrap(); assert_eq!( None, crate::test::first_significant_code_diff( expect_exported_flags_content, &String::from_utf8(file.contents.clone()).unwrap() ), "ExportedFlags content is not correct" ); } #[test] fn test_format_java_method_name() { let expected = "someSnakeName"; let input = "____some_snake___name____"; let formatted_name = format_java_method_name(input); assert_eq!(expected, formatted_name); let input = "someSnakeName"; let formatted_name = format_java_method_name(input); assert_eq!(expected, formatted_name); let input = "SomeSnakeName"; let formatted_name = format_java_method_name(input); assert_eq!(expected, formatted_name); let input = "SomeSnakeName_"; let formatted_name = format_java_method_name(input); assert_eq!(expected, formatted_name); let input = "_SomeSnakeName"; let formatted_name = format_java_method_name(input); assert_eq!(expected, formatted_name); } #[test] fn test_format_property_name() { let expected = "mPropertiesSomeSnakeName"; let input = "____some_snake___name____"; let formatted_name = format_property_name(input); assert_eq!(expected, formatted_name); let input = "someSnakeName"; let formatted_name = format_property_name(input); assert_eq!(expected, formatted_name); let input = "SomeSnakeName"; let formatted_name = format_property_name(input); assert_eq!(expected, formatted_name); let input = "SomeSnakeName_"; let formatted_name = format_property_name(input); assert_eq!(expected, formatted_name); } } ================================================ FILE: tools/aconfig/aconfig/src/codegen/mod.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pub mod cpp; pub mod java; pub mod rust; use aconfig_protos::{is_valid_name_ident, is_valid_package_ident}; use anyhow::{ensure, Result}; use clap::ValueEnum; pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result { ensure!(is_valid_package_ident(package), "bad package"); ensure!(is_valid_name_ident(flag_name), "bad flag name"); Ok(format!("{}.{}", package, flag_name)) } #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)] pub enum CodegenMode { Exported, ForceReadOnly, Production, Test, } impl std::fmt::Display for CodegenMode { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { CodegenMode::Exported => write!(f, "exported"), CodegenMode::ForceReadOnly => write!(f, "force-read-only"), CodegenMode::Production => write!(f, "production"), CodegenMode::Test => write!(f, "test"), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_create_device_config_ident() { assert_eq!( "com.foo.bar.some_flag", create_device_config_ident("com.foo.bar", "some_flag").unwrap() ); } } ================================================ FILE: tools/aconfig/aconfig/src/codegen/rust.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::Result; use serde::Serialize; use tinytemplate::TinyTemplate; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag}; use std::collections::HashMap; use crate::codegen; use crate::codegen::CodegenMode; use crate::commands::{should_include_flag, OutputFile}; pub fn generate_rust_code( package: &str, flag_ids: HashMap, parsed_flags_iter: I, codegen_mode: CodegenMode, package_fingerprint: Option, ) -> Result where I: Iterator, { let template_flags: Vec = parsed_flags_iter .map(|pf| TemplateParsedFlag::new(package, flag_ids.clone(), &pf)) .collect(); let has_readwrite = template_flags.iter().any(|item| item.readwrite); let container = (template_flags.first().expect("zero template flags").container).to_string(); let use_package_fingerprint = package_fingerprint.is_some(); let context = TemplateContext { package: package.to_string(), template_flags, modules: package.split('.').map(|s| s.to_string()).collect::>(), has_readwrite, container, use_package_fingerprint, package_fingerprint: package_fingerprint.unwrap_or_default(), }; let mut template = TinyTemplate::new(); template.add_template( "rust_code_gen", match codegen_mode { CodegenMode::Test => include_str!("../../templates/rust_test.template"), CodegenMode::Exported | CodegenMode::ForceReadOnly | CodegenMode::Production => { include_str!("../../templates/rust.template") } }, )?; let contents = template.render("rust_code_gen", &context)?; let path = ["src", "lib.rs"].iter().collect(); Ok(OutputFile { contents: contents.into(), path }) } #[derive(Serialize)] struct TemplateContext { pub package: String, pub template_flags: Vec, pub modules: Vec, pub has_readwrite: bool, pub container: String, pub use_package_fingerprint: bool, pub package_fingerprint: u64, } #[derive(Serialize)] struct TemplateParsedFlag { pub readwrite: bool, pub default_value: String, pub name: String, pub container: String, pub flag_offset: u16, pub device_config_namespace: String, pub device_config_flag: String, } impl TemplateParsedFlag { #[allow(clippy::nonminimal_bool)] fn new(package: &str, flag_offsets: HashMap, pf: &ProtoParsedFlag) -> Self { let flag_offset = match flag_offsets.get(pf.name()) { Some(offset) => offset, None => { // System/vendor/product RO+disabled flags have no offset in storage files. // Assign placeholder value. if !should_include_flag(pf) { &0 } // All other flags _must_ have an offset. else { panic!("{}", format!("missing flag offset for {}", pf.name())); } } }; Self { readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE, default_value: match pf.state() { ProtoFlagState::ENABLED => "true".to_string(), ProtoFlagState::DISABLED => "false".to_string(), }, name: pf.name().to_string(), container: pf.container().to_string(), flag_offset: *flag_offset, device_config_namespace: pf.namespace().to_string(), device_config_flag: codegen::create_device_config_ident(package, pf.name()) .expect("values checked at flag parse time"), } } } #[cfg(test)] mod tests { use super::*; const PROD_EXPECTED: &str = r#" //! codegenerated rust flag lib use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context}; use std::path::Path; use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; /// flag provider pub struct FlagProvider; static PACKAGE_CONTEXT: LazyLock, AconfigStorageError>> = LazyLock::new(|| unsafe { get_mapped_storage_file("system", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test")) }); static FLAG_VAL_MAP: LazyLock> = LazyLock::new(|| unsafe { get_mapped_storage_file("system", StorageFileType::FlagVal) }); /// flag value cache for disabled_rw static CACHED_disabled_rw: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 0) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for disabled_rw_exported static CACHED_disabled_rw_exported: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 1) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for disabled_rw_in_other_namespace static CACHED_disabled_rw_in_other_namespace: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 2) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for enabled_rw static CACHED_enabled_rw: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 7) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return true; } } }); impl FlagProvider { /// query flag disabled_ro pub fn disabled_ro(&self) -> bool { false } /// query flag disabled_rw pub fn disabled_rw(&self) -> bool { *CACHED_disabled_rw } /// query flag disabled_rw_exported pub fn disabled_rw_exported(&self) -> bool { *CACHED_disabled_rw_exported } /// query flag disabled_rw_in_other_namespace pub fn disabled_rw_in_other_namespace(&self) -> bool { *CACHED_disabled_rw_in_other_namespace } /// query flag enabled_fixed_ro pub fn enabled_fixed_ro(&self) -> bool { true } /// query flag enabled_fixed_ro_exported pub fn enabled_fixed_ro_exported(&self) -> bool { true } /// query flag enabled_ro pub fn enabled_ro(&self) -> bool { true } /// query flag enabled_ro_exported pub fn enabled_ro_exported(&self) -> bool { true } /// query flag enabled_rw pub fn enabled_rw(&self) -> bool { *CACHED_enabled_rw } } /// flag provider pub static PROVIDER: FlagProvider = FlagProvider; /// query flag disabled_ro #[inline(always)] pub fn disabled_ro() -> bool { false } /// query flag disabled_rw #[inline(always)] pub fn disabled_rw() -> bool { PROVIDER.disabled_rw() } /// query flag disabled_rw_exported #[inline(always)] pub fn disabled_rw_exported() -> bool { PROVIDER.disabled_rw_exported() } /// query flag disabled_rw_in_other_namespace #[inline(always)] pub fn disabled_rw_in_other_namespace() -> bool { PROVIDER.disabled_rw_in_other_namespace() } /// query flag enabled_fixed_ro #[inline(always)] pub fn enabled_fixed_ro() -> bool { true } /// query flag enabled_fixed_ro_exported #[inline(always)] pub fn enabled_fixed_ro_exported() -> bool { true } /// query flag enabled_ro #[inline(always)] pub fn enabled_ro() -> bool { true } /// query flag enabled_ro_exported #[inline(always)] pub fn enabled_ro_exported() -> bool { true } /// query flag enabled_rw #[inline(always)] pub fn enabled_rw() -> bool { PROVIDER.enabled_rw() } "#; const TEST_EXPECTED: &str = r#" //! codegenerated rust flag lib use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context}; use std::collections::BTreeMap; use std::path::Path; use std::io::Write; use std::sync::{LazyLock, Mutex}; use log::{log, LevelFilter, Level}; /// flag provider pub struct FlagProvider { overrides: BTreeMap<&'static str, bool>, } static PACKAGE_CONTEXT: LazyLock, AconfigStorageError>> = LazyLock::new(|| unsafe { get_mapped_storage_file("system", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test")) }); static FLAG_VAL_MAP: LazyLock> = LazyLock::new(|| unsafe { get_mapped_storage_file("system", StorageFileType::FlagVal) }); /// flag value cache for disabled_rw static CACHED_disabled_rw: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 0) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for disabled_rw_exported static CACHED_disabled_rw_exported: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 1) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for disabled_rw_in_other_namespace static CACHED_disabled_rw_in_other_namespace: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 2) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return false; } } }); /// flag value cache for enabled_rw static CACHED_enabled_rw: LazyLock = LazyLock::new(|| { // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: {err}")) .and_then(|flag_val_map| { PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: {err}")) .and_then(|package_context| { match package_context { Some(context) => { get_boolean_flag_value(&flag_val_map, context.boolean_start_index + 7) .map_err(|err| format!("failed to get flag: {err}")) }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); Err(format!("failed to flag package com.android.aconfig.test")) } } }) }); match flag_value_result { Ok(flag_value) => { return flag_value; }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); return true; } } }); impl FlagProvider { /// query flag disabled_ro pub fn disabled_ro(&self) -> bool { self.overrides.get("disabled_ro").copied().unwrap_or( false ) } /// set flag disabled_ro pub fn set_disabled_ro(&mut self, val: bool) { self.overrides.insert("disabled_ro", val); } /// query flag disabled_rw pub fn disabled_rw(&self) -> bool { self.overrides.get("disabled_rw").copied().unwrap_or( *CACHED_disabled_rw ) } /// set flag disabled_rw pub fn set_disabled_rw(&mut self, val: bool) { self.overrides.insert("disabled_rw", val); } /// query flag disabled_rw_exported pub fn disabled_rw_exported(&self) -> bool { self.overrides.get("disabled_rw_exported").copied().unwrap_or( *CACHED_disabled_rw_exported ) } /// set flag disabled_rw_exported pub fn set_disabled_rw_exported(&mut self, val: bool) { self.overrides.insert("disabled_rw_exported", val); } /// query flag disabled_rw_in_other_namespace pub fn disabled_rw_in_other_namespace(&self) -> bool { self.overrides.get("disabled_rw_in_other_namespace").copied().unwrap_or( *CACHED_disabled_rw_in_other_namespace ) } /// set flag disabled_rw_in_other_namespace pub fn set_disabled_rw_in_other_namespace(&mut self, val: bool) { self.overrides.insert("disabled_rw_in_other_namespace", val); } /// query flag enabled_fixed_ro pub fn enabled_fixed_ro(&self) -> bool { self.overrides.get("enabled_fixed_ro").copied().unwrap_or( true ) } /// set flag enabled_fixed_ro pub fn set_enabled_fixed_ro(&mut self, val: bool) { self.overrides.insert("enabled_fixed_ro", val); } /// query flag enabled_fixed_ro_exported pub fn enabled_fixed_ro_exported(&self) -> bool { self.overrides.get("enabled_fixed_ro_exported").copied().unwrap_or( true ) } /// set flag enabled_fixed_ro_exported pub fn set_enabled_fixed_ro_exported(&mut self, val: bool) { self.overrides.insert("enabled_fixed_ro_exported", val); } /// query flag enabled_ro pub fn enabled_ro(&self) -> bool { self.overrides.get("enabled_ro").copied().unwrap_or( true ) } /// set flag enabled_ro pub fn set_enabled_ro(&mut self, val: bool) { self.overrides.insert("enabled_ro", val); } /// query flag enabled_ro_exported pub fn enabled_ro_exported(&self) -> bool { self.overrides.get("enabled_ro_exported").copied().unwrap_or( true ) } /// set flag enabled_ro_exported pub fn set_enabled_ro_exported(&mut self, val: bool) { self.overrides.insert("enabled_ro_exported", val); } /// query flag enabled_rw pub fn enabled_rw(&self) -> bool { self.overrides.get("enabled_rw").copied().unwrap_or( *CACHED_enabled_rw ) } /// set flag enabled_rw pub fn set_enabled_rw(&mut self, val: bool) { self.overrides.insert("enabled_rw", val); } /// clear all flag overrides pub fn reset_flags(&mut self) { self.overrides.clear(); } } /// flag provider pub static PROVIDER: Mutex = Mutex::new( FlagProvider {overrides: BTreeMap::new()} ); /// query flag disabled_ro #[inline(always)] pub fn disabled_ro() -> bool { PROVIDER.lock().unwrap().disabled_ro() } /// set flag disabled_ro #[inline(always)] pub fn set_disabled_ro(val: bool) { PROVIDER.lock().unwrap().set_disabled_ro(val); } /// query flag disabled_rw #[inline(always)] pub fn disabled_rw() -> bool { PROVIDER.lock().unwrap().disabled_rw() } /// set flag disabled_rw #[inline(always)] pub fn set_disabled_rw(val: bool) { PROVIDER.lock().unwrap().set_disabled_rw(val); } /// query flag disabled_rw_exported #[inline(always)] pub fn disabled_rw_exported() -> bool { PROVIDER.lock().unwrap().disabled_rw_exported() } /// set flag disabled_rw_exported #[inline(always)] pub fn set_disabled_rw_exported(val: bool) { PROVIDER.lock().unwrap().set_disabled_rw_exported(val); } /// query flag disabled_rw_in_other_namespace #[inline(always)] pub fn disabled_rw_in_other_namespace() -> bool { PROVIDER.lock().unwrap().disabled_rw_in_other_namespace() } /// set flag disabled_rw_in_other_namespace #[inline(always)] pub fn set_disabled_rw_in_other_namespace(val: bool) { PROVIDER.lock().unwrap().set_disabled_rw_in_other_namespace(val); } /// query flag enabled_fixed_ro #[inline(always)] pub fn enabled_fixed_ro() -> bool { PROVIDER.lock().unwrap().enabled_fixed_ro() } /// set flag enabled_fixed_ro #[inline(always)] pub fn set_enabled_fixed_ro(val: bool) { PROVIDER.lock().unwrap().set_enabled_fixed_ro(val); } /// query flag enabled_fixed_ro_exported #[inline(always)] pub fn enabled_fixed_ro_exported() -> bool { PROVIDER.lock().unwrap().enabled_fixed_ro_exported() } /// set flag enabled_fixed_ro_exported #[inline(always)] pub fn set_enabled_fixed_ro_exported(val: bool) { PROVIDER.lock().unwrap().set_enabled_fixed_ro_exported(val); } /// query flag enabled_ro #[inline(always)] pub fn enabled_ro() -> bool { PROVIDER.lock().unwrap().enabled_ro() } /// set flag enabled_ro #[inline(always)] pub fn set_enabled_ro(val: bool) { PROVIDER.lock().unwrap().set_enabled_ro(val); } /// query flag enabled_ro_exported #[inline(always)] pub fn enabled_ro_exported() -> bool { PROVIDER.lock().unwrap().enabled_ro_exported() } /// set flag enabled_ro_exported #[inline(always)] pub fn set_enabled_ro_exported(val: bool) { PROVIDER.lock().unwrap().set_enabled_ro_exported(val); } /// query flag enabled_rw #[inline(always)] pub fn enabled_rw() -> bool { PROVIDER.lock().unwrap().enabled_rw() } /// set flag enabled_rw #[inline(always)] pub fn set_enabled_rw(val: bool) { PROVIDER.lock().unwrap().set_enabled_rw(val); } /// clear all flag override pub fn reset_flags() { PROVIDER.lock().unwrap().reset_flags() } "#; const FORCE_READ_ONLY_EXPECTED: &str = r#" //! codegenerated rust flag lib use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context}; use std::path::Path; use std::io::Write; use std::sync::LazyLock; use log::{log, LevelFilter, Level}; /// flag provider pub struct FlagProvider; impl FlagProvider { /// query flag disabled_ro pub fn disabled_ro(&self) -> bool { false } /// query flag disabled_rw pub fn disabled_rw(&self) -> bool { false } /// query flag disabled_rw_in_other_namespace pub fn disabled_rw_in_other_namespace(&self) -> bool { false } /// query flag enabled_fixed_ro pub fn enabled_fixed_ro(&self) -> bool { true } /// query flag enabled_ro pub fn enabled_ro(&self) -> bool { true } /// query flag enabled_rw pub fn enabled_rw(&self) -> bool { true } } /// flag provider pub static PROVIDER: FlagProvider = FlagProvider; /// query flag disabled_ro #[inline(always)] pub fn disabled_ro() -> bool { false } /// query flag disabled_rw #[inline(always)] pub fn disabled_rw() -> bool { false } /// query flag disabled_rw_in_other_namespace #[inline(always)] pub fn disabled_rw_in_other_namespace() -> bool { false } /// query flag enabled_fixed_ro #[inline(always)] pub fn enabled_fixed_ro() -> bool { true } /// query flag enabled_ro #[inline(always)] pub fn enabled_ro() -> bool { true } /// query flag enabled_rw #[inline(always)] pub fn enabled_rw() -> bool { true } "#; use crate::commands::assign_flag_ids; fn test_generate_rust_code(mode: CodegenMode, expected: &str) { let parsed_flags = crate::test::parse_test_flags(); let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); let flag_ids = assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); let generated = generate_rust_code( crate::test::TEST_PACKAGE, flag_ids, modified_parsed_flags.into_iter(), mode, None, ) .unwrap(); assert_eq!("src/lib.rs", format!("{}", generated.path.display())); crate::test::assert_no_significant_code_diff( expected, &String::from_utf8(generated.contents).unwrap(), ); } #[test] fn test_generate_rust_code_for_prod() { test_generate_rust_code(CodegenMode::Production, PROD_EXPECTED); } #[test] fn test_generate_rust_code_for_test() { test_generate_rust_code(CodegenMode::Test, TEST_EXPECTED); } #[test] fn test_generate_rust_code_for_force_read_only() { test_generate_rust_code(CodegenMode::ForceReadOnly, FORCE_READ_ONLY_EXPECTED); } } ================================================ FILE: tools/aconfig/aconfig/src/commands.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::{bail, ensure, Context, Result}; use convert_finalized_flags::FinalizedFlagMap; use itertools::Itertools; use protobuf::Message; use std::collections::HashMap; use std::hash::Hasher; use std::io::Read; use std::path::PathBuf; use crate::codegen::cpp::generate_cpp_code; use crate::codegen::java::{generate_java_code, JavaCodegenConfig}; use crate::codegen::rust::generate_rust_code; use crate::codegen::CodegenMode; use crate::dump::{DumpFormat, DumpPredicate}; use crate::storage::generate_storage_file; use aconfig_protos::{ ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags, ProtoTracepoint, }; use aconfig_storage_file::sip_hasher13::SipHasher13; use aconfig_storage_file::StorageFileType; pub struct Input { pub source: String, pub reader: Box, } impl Input { fn try_parse_flags(&mut self) -> Result { let mut buffer = Vec::new(); self.reader .read_to_end(&mut buffer) .with_context(|| format!("failed to read {}", self.source))?; aconfig_protos::parsed_flags::try_from_binary_proto(&buffer) .with_context(|| self.error_context()) } fn error_context(&self) -> String { format!("failed to parse {}", self.source) } } pub struct OutputFile { pub path: PathBuf, // relative to some root directory only main knows about pub contents: Vec, } pub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED; pub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE; pub fn parse_flags( package: &str, container: Option<&str>, declarations: Vec, values: Vec, default_permission: ProtoFlagPermission, allow_read_write: bool, ) -> Result> { let mut parsed_flags = ProtoParsedFlags::new(); for mut input in declarations { let mut contents = String::new(); input .reader .read_to_string(&mut contents) .with_context(|| format!("failed to read {}", input.source))?; let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents) .with_context(|| input.error_context())?; ensure!( package == flag_declarations.package(), "failed to parse {}: expected package {}, got {}", input.source, package, flag_declarations.package() ); if let Some(c) = container { ensure!( c == flag_declarations.container(), "failed to parse {}: expected container {}, got {}", input.source, c, flag_declarations.container() ); } for mut flag_declaration in flag_declarations.flag.into_iter() { aconfig_protos::flag_declaration::verify_fields(&flag_declaration) .with_context(|| input.error_context())?; // create ParsedFlag using FlagDeclaration and default values let mut parsed_flag = ProtoParsedFlag::new(); if let Some(c) = container { parsed_flag.set_container(c.to_string()); } parsed_flag.set_package(package.to_string()); parsed_flag.set_name(flag_declaration.take_name()); parsed_flag.set_namespace(flag_declaration.take_namespace()); parsed_flag.set_description(flag_declaration.take_description()); parsed_flag.bug.append(&mut flag_declaration.bug); parsed_flag.set_state(DEFAULT_FLAG_STATE); let flag_permission = if flag_declaration.is_fixed_read_only() { ProtoFlagPermission::READ_ONLY } else { default_permission }; parsed_flag.set_permission(flag_permission); parsed_flag.set_is_fixed_read_only(flag_declaration.is_fixed_read_only()); parsed_flag.set_is_exported(flag_declaration.is_exported()); let mut tracepoint = ProtoTracepoint::new(); tracepoint.set_source(input.source.clone()); tracepoint.set_state(DEFAULT_FLAG_STATE); tracepoint.set_permission(flag_permission); parsed_flag.trace.push(tracepoint); let mut metadata = ProtoFlagMetadata::new(); let purpose = flag_declaration.metadata.purpose(); metadata.set_purpose(purpose); parsed_flag.metadata = Some(metadata).into(); // verify ParsedFlag looks reasonable aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?; // verify ParsedFlag can be added ensure!( parsed_flags.parsed_flag.iter().all(|other| other.name() != parsed_flag.name()), "failed to declare flag {} from {}: flag already declared", parsed_flag.name(), input.source ); // add ParsedFlag to ParsedFlags parsed_flags.parsed_flag.push(parsed_flag); } } for mut input in values { let mut contents = String::new(); input .reader .read_to_string(&mut contents) .with_context(|| format!("failed to read {}", input.source))?; let flag_values = aconfig_protos::flag_values::try_from_text_proto(&contents) .with_context(|| input.error_context())?; for flag_value in flag_values.flag_value.into_iter() { aconfig_protos::flag_value::verify_fields(&flag_value) .with_context(|| input.error_context())?; let Some(parsed_flag) = parsed_flags .parsed_flag .iter_mut() .find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name()) else { // (silently) skip unknown flags continue; }; ensure!( !parsed_flag.is_fixed_read_only() || flag_value.permission() == ProtoFlagPermission::READ_ONLY, "failed to set permission of flag {}, since this flag is fixed read only flag", flag_value.name() ); parsed_flag.set_state(flag_value.state()); parsed_flag.set_permission(flag_value.permission()); let mut tracepoint = ProtoTracepoint::new(); tracepoint.set_source(input.source.clone()); tracepoint.set_state(flag_value.state()); tracepoint.set_permission(flag_value.permission()); parsed_flag.trace.push(tracepoint); } } if !allow_read_write { if let Some(pf) = parsed_flags .parsed_flag .iter() .find(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE) { bail!("flag {} has permission READ_WRITE, but allow_read_write is false", pf.name()); } } // Create a sorted parsed_flags aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags); aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?; let mut output = Vec::new(); parsed_flags.write_to_vec(&mut output)?; Ok(output) } pub fn create_java_lib( mut input: Input, codegen_mode: CodegenMode, allow_instrumentation: bool, new_exported: bool, single_exported_file: bool, finalized_flags: FinalizedFlagMap, ) -> Result> { let parsed_flags = input.try_parse_flags()?; let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?; let Some(package) = find_unique_package(&modified_parsed_flags) else { bail!("no parsed flags, or the parsed flags use different packages"); }; let package = package.to_string(); let mut flag_names = extract_flag_names(parsed_flags)?; let package_fingerprint = compute_flags_fingerprint(&mut flag_names); let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; let config = JavaCodegenConfig { codegen_mode, flag_ids, allow_instrumentation, package_fingerprint, new_exported, single_exported_file, finalized_flags, }; generate_java_code(&package, modified_parsed_flags.into_iter(), config) } pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result> { // TODO(327420679): Enable export mode for native flag library ensure!( codegen_mode != CodegenMode::Exported, "Exported mode for generated c/c++ flag library is disabled" ); let parsed_flags = input.try_parse_flags()?; let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?; let Some(package) = find_unique_package(&modified_parsed_flags) else { bail!("no parsed flags, or the parsed flags use different packages"); }; let package = package.to_string(); let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode, flag_ids) } pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result { // // TODO(327420679): Enable export mode for native flag library ensure!( codegen_mode != CodegenMode::Exported, "Exported mode for generated rust flag library is disabled" ); let parsed_flags = input.try_parse_flags()?; let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags.clone(), codegen_mode)?; let Some(package) = find_unique_package(&modified_parsed_flags) else { bail!("no parsed flags, or the parsed flags use different packages"); }; let package = package.to_string(); let package_fingerprint: Option = if cfg!(enable_fingerprint_rust) { let mut flag_names = extract_flag_names(parsed_flags)?; Some(compute_flags_fingerprint(&mut flag_names)) } else { None }; let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; generate_rust_code( &package, flag_ids, modified_parsed_flags.into_iter(), codegen_mode, package_fingerprint, ) } pub fn create_storage( caches: Vec, container: &str, file: &StorageFileType, version: u32, ) -> Result> { let parsed_flags_vec: Vec = caches.into_iter().map(|mut input| input.try_parse_flags()).collect::>>()?; generate_storage_file(container, parsed_flags_vec.iter(), file, version) } pub fn create_device_config_defaults(mut input: Input) -> Result> { let parsed_flags = input.try_parse_flags()?; let mut output = Vec::new(); for parsed_flag in parsed_flags .parsed_flag .into_iter() .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE) { let line = format!( "{}:{}={}\n", parsed_flag.namespace(), parsed_flag.fully_qualified_name(), match parsed_flag.state() { ProtoFlagState::ENABLED => "enabled", ProtoFlagState::DISABLED => "disabled", } ); output.extend_from_slice(line.as_bytes()); } Ok(output) } pub fn create_device_config_sysprops(mut input: Input) -> Result> { let parsed_flags = input.try_parse_flags()?; let mut output = Vec::new(); for parsed_flag in parsed_flags .parsed_flag .into_iter() .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE) { let line = format!( "persist.device_config.{}={}\n", parsed_flag.fully_qualified_name(), match parsed_flag.state() { ProtoFlagState::ENABLED => "true", ProtoFlagState::DISABLED => "false", } ); output.extend_from_slice(line.as_bytes()); } Ok(output) } pub fn dump_parsed_flags( mut input: Vec, format: DumpFormat, filters: &[&str], dedup: bool, ) -> Result> { let individually_parsed_flags: Result> = input.iter_mut().map(|i| i.try_parse_flags()).collect(); let parsed_flags: ProtoParsedFlags = aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?; let filters: Vec> = if filters.is_empty() { vec![Box::new(|_| true)] } else { filters .iter() .map(|f| crate::dump::create_filter_predicate(f)) .collect::>>()? }; crate::dump::dump_parsed_flags( parsed_flags.parsed_flag.into_iter().filter(|flag| filters.iter().any(|p| p(flag))), format, ) } fn find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str> { let package = parsed_flags.first().map(|pf| pf.package())?; if parsed_flags.iter().any(|pf| pf.package() != package) { return None; } Some(package) } pub fn modify_parsed_flags_based_on_mode( parsed_flags: ProtoParsedFlags, codegen_mode: CodegenMode, ) -> Result> { fn exported_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag { parsed_flag.set_state(ProtoFlagState::DISABLED); parsed_flag.set_permission(ProtoFlagPermission::READ_WRITE); parsed_flag.set_is_fixed_read_only(false); parsed_flag } fn force_read_only_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag { parsed_flag.set_permission(ProtoFlagPermission::READ_ONLY); parsed_flag } let modified_parsed_flags: Vec<_> = match codegen_mode { CodegenMode::Exported => parsed_flags .parsed_flag .into_iter() .filter(|pf| pf.is_exported()) .map(exported_mode_flag_modifier) .collect(), CodegenMode::ForceReadOnly => parsed_flags .parsed_flag .into_iter() .filter(|pf| !pf.is_exported()) .map(force_read_only_mode_flag_modifier) .collect(), CodegenMode::Production | CodegenMode::Test => { parsed_flags.parsed_flag.into_iter().collect() } }; if modified_parsed_flags.is_empty() { bail!("{codegen_mode} library contains no {codegen_mode} flags"); } Ok(modified_parsed_flags) } pub fn assign_flag_ids<'a, I>(package: &str, parsed_flags_iter: I) -> Result> where I: Iterator + Clone, { assert!(parsed_flags_iter.clone().tuple_windows().all(|(a, b)| a.name() <= b.name())); let mut flag_ids = HashMap::new(); let mut flag_idx = 0; for pf in parsed_flags_iter { if package != pf.package() { return Err(anyhow::anyhow!("encountered a flag not in current package")); } // put a cap on how many flags a package can contain to 65535 if flag_idx > u16::MAX as u32 { return Err(anyhow::anyhow!("the number of flags in a package cannot exceed 65535")); } if should_include_flag(pf) { flag_ids.insert(pf.name().to_string(), flag_idx as u16); flag_idx += 1; } } Ok(flag_ids) } // Creates a fingerprint of the flag names (which requires sorting the vector). // Fingerprint is used by both codegen and storage files. pub fn compute_flags_fingerprint(flag_names: &mut Vec) -> u64 { flag_names.sort(); let mut hasher = SipHasher13::new(); for flag in flag_names { hasher.write(flag.as_bytes()); } hasher.finish() } // Converts ProtoParsedFlags into a vector of strings containing all of the flag // names. Helper fn for creating fingerprint for codegen files. Flags must all // belong to the same package. fn extract_flag_names(flags: ProtoParsedFlags) -> Result> { let separated_flags: Vec = flags.parsed_flag.into_iter().collect::>(); // All flags must belong to the same package as the fingerprint is per-package. let Some(_package) = find_unique_package(&separated_flags) else { bail!("No parsed flags, or the parsed flags use different packages."); }; Ok(separated_flags .into_iter() .filter(should_include_flag) .map(|flag| flag.name.unwrap()) .collect::>()) } // Exclude system/vendor/product flags that are RO+disabled. pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool { let should_filter_container = pf.container == Some("vendor".to_string()) || pf.container == Some("system".to_string()) || pf.container == Some("system_ext".to_string()) || pf.container == Some("product".to_string()); let disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into()) && pf.permission == Some(ProtoFlagPermission::READ_ONLY.into()); !should_filter_container || !disabled_ro } #[cfg(test)] mod tests { use super::*; use aconfig_protos::ProtoFlagPurpose; #[test] fn test_offset_fingerprint() { let parsed_flags = crate::test::parse_test_flags(); let expected_fingerprint: u64 = 11551379960324242360; let mut extracted_flags = extract_flag_names(parsed_flags).unwrap(); let hash_result = compute_flags_fingerprint(&mut extracted_flags); assert_eq!(hash_result, expected_fingerprint); } #[test] fn test_offset_fingerprint_matches_from_package() { let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); // All test flags are in the same package, so fingerprint from all of them. let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap(); let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags); let mut flag_names_vec = parsed_flags .parsed_flag .clone() .into_iter() .filter(should_include_flag) .map(|flag| flag.name.unwrap()) .map(String::from) .collect::>(); let result_from_names = compute_flags_fingerprint(&mut flag_names_vec); // Assert the same hash is generated for each case. assert_eq!(result_from_parsed_flags, result_from_names); } #[test] fn test_offset_fingerprint_different_packages_does_not_match() { // Parse flags from two packages. let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); let second_parsed_flags = crate::test::parse_second_package_flags(); let mut extracted_flags = extract_flag_names(parsed_flags).unwrap(); let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags); let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap(); let second_result = compute_flags_fingerprint(&mut second_extracted_flags); // Different flags should have a different fingerprint. assert_ne!(result_from_parsed_flags, second_result); } #[test] fn test_parse_flags() { let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap(); let enabled_ro = parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap(); assert!(aconfig_protos::parsed_flag::verify_fields(enabled_ro).is_ok()); assert_eq!("com.android.aconfig.test", enabled_ro.package()); assert_eq!("enabled_ro", enabled_ro.name()); assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description()); assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state()); assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission()); assert_eq!(ProtoFlagPurpose::PURPOSE_BUGFIX, enabled_ro.metadata.purpose()); assert_eq!(3, enabled_ro.trace.len()); assert!(!enabled_ro.is_fixed_read_only()); assert_eq!("tests/test.aconfig", enabled_ro.trace[0].source()); assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state()); assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission()); assert_eq!("tests/first.values", enabled_ro.trace[1].source()); assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[1].state()); assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[1].permission()); assert_eq!("tests/second.values", enabled_ro.trace[2].source()); assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state()); assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission()); assert_eq!(9, parsed_flags.parsed_flag.len()); for pf in parsed_flags.parsed_flag.iter() { if pf.name().starts_with("enabled_fixed_ro") { continue; } let first = pf.trace.first().unwrap(); assert_eq!(DEFAULT_FLAG_STATE, first.state()); assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission()); let last = pf.trace.last().unwrap(); assert_eq!(pf.state(), last.state()); assert_eq!(pf.permission(), last.permission()); } let enabled_fixed_ro = parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_fixed_ro").unwrap(); assert!(enabled_fixed_ro.is_fixed_read_only()); assert_eq!(ProtoFlagState::ENABLED, enabled_fixed_ro.state()); assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.permission()); assert_eq!(2, enabled_fixed_ro.trace.len()); assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[0].permission()); assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[1].permission()); } #[test] fn test_parse_flags_setting_default() { let first_flag = r#" package: "com.first" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "momery".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let value: Vec = vec![]; let flags_bytes = crate::commands::parse_flags( "com.first", None, declaration, value, ProtoFlagPermission::READ_ONLY, true, ) .unwrap(); let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap(); assert_eq!(1, parsed_flags.parsed_flag.len()); let parsed_flag = parsed_flags.parsed_flag.first().unwrap(); assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state()); assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission()); } #[test] fn test_parse_flags_package_mismatch_between_declaration_and_command_line() { let first_flag = r#" package: "com.declaration.package" container: "first.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let value: Vec = vec![]; let error = crate::commands::parse_flags( "com.argument.package", Some("first.container"), declaration, value, ProtoFlagPermission::READ_WRITE, true, ) .unwrap_err(); assert_eq!( format!("{:?}", error), "failed to parse memory: expected package com.argument.package, got com.declaration.package" ); } #[test] fn test_parse_flags_container_mismatch_between_declaration_and_command_line() { let first_flag = r#" package: "com.first" container: "declaration.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let value: Vec = vec![]; let error = crate::commands::parse_flags( "com.first", Some("argument.container"), declaration, value, ProtoFlagPermission::READ_WRITE, true, ) .unwrap_err(); assert_eq!( format!("{:?}", error), "failed to parse memory: expected container argument.container, got declaration.container" ); } #[test] fn test_parse_flags_no_allow_read_write_default_error() { let first_flag = r#" package: "com.first" container: "com.first.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let error = crate::commands::parse_flags( "com.first", Some("com.first.container"), declaration, vec![], ProtoFlagPermission::READ_WRITE, false, ) .unwrap_err(); assert_eq!( format!("{:?}", error), "flag first has permission READ_WRITE, but allow_read_write is false" ); } #[test] fn test_parse_flags_no_allow_read_write_value_error() { let first_flag = r#" package: "com.first" container: "com.first.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let first_flag_value = r#" flag_value { package: "com.first" name: "first" state: DISABLED permission: READ_WRITE } "#; let value = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag_value.as_bytes()), }]; let error = crate::commands::parse_flags( "com.first", Some("com.first.container"), declaration, value, ProtoFlagPermission::READ_ONLY, false, ) .unwrap_err(); assert_eq!( format!("{:?}", error), "flag first has permission READ_WRITE, but allow_read_write is false" ); } #[test] fn test_parse_flags_no_allow_read_write_success() { let first_flag = r#" package: "com.first" container: "com.first.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let first_flag_value = r#" flag_value { package: "com.first" name: "first" state: DISABLED permission: READ_ONLY } "#; let value = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag_value.as_bytes()), }]; let flags_bytes = crate::commands::parse_flags( "com.first", Some("com.first.container"), declaration, value, ProtoFlagPermission::READ_ONLY, false, ) .unwrap(); let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap(); assert_eq!(1, parsed_flags.parsed_flag.len()); let parsed_flag = parsed_flags.parsed_flag.first().unwrap(); assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state()); assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission()); } #[test] fn test_parse_flags_override_fixed_read_only() { let first_flag = r#" package: "com.first" container: "com.first.container" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" is_fixed_read_only: true } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }]; let first_flag_value = r#" flag_value { package: "com.first" name: "first" state: DISABLED permission: READ_WRITE } "#; let value = vec![Input { source: "memory".to_string(), reader: Box::new(first_flag_value.as_bytes()), }]; let error = crate::commands::parse_flags( "com.first", Some("com.first.container"), declaration, value, ProtoFlagPermission::READ_WRITE, true, ) .unwrap_err(); assert_eq!( format!("{:?}", error), "failed to set permission of flag first, since this flag is fixed read only flag" ); } #[test] fn test_parse_flags_metadata() { let metadata_flag = r#" package: "com.first" flag { name: "first" namespace: "first_ns" description: "This is the description of this feature flag." bug: "123" metadata { purpose: PURPOSE_FEATURE } } "#; let declaration = vec![Input { source: "memory".to_string(), reader: Box::new(metadata_flag.as_bytes()), }]; let value: Vec = vec![]; let flags_bytes = crate::commands::parse_flags( "com.first", None, declaration, value, ProtoFlagPermission::READ_ONLY, true, ) .unwrap(); let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap(); assert_eq!(1, parsed_flags.parsed_flag.len()); let parsed_flag = parsed_flags.parsed_flag.first().unwrap(); assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose()); } #[test] fn test_create_device_config_defaults() { let input = parse_test_flags_as_input(); let bytes = create_device_config_defaults(input).unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.disabled_rw_exported=disabled\nother_namespace:com.android.aconfig.test.disabled_rw_in_other_namespace=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text); } #[test] fn test_create_device_config_sysprops() { let input = parse_test_flags_as_input(); let bytes = create_device_config_sysprops(input).unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.disabled_rw_exported=false\npersist.device_config.com.android.aconfig.test.disabled_rw_in_other_namespace=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text); } #[test] fn test_dump() { let input = parse_test_flags_as_input(); let bytes = dump_parsed_flags( vec![input], DumpFormat::Custom("{fully_qualified_name}".to_string()), &[], false, ) .unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert!(text.contains("com.android.aconfig.test.disabled_ro")); } #[test] fn test_dump_multiple_filters() { let input = parse_test_flags_as_input(); let bytes = dump_parsed_flags( vec![input], DumpFormat::Custom("{fully_qualified_name}".to_string()), &["container:system+state:ENABLED", "container:system+permission:READ_WRITE"], false, ) .unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); let expected_flag_list = &[ "com.android.aconfig.test.disabled_rw", "com.android.aconfig.test.disabled_rw_exported", "com.android.aconfig.test.disabled_rw_in_other_namespace", "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", "com.android.aconfig.test.enabled_rw", ]; assert_eq!(expected_flag_list.map(|s| format!("{}\n", s)).join(""), text); } #[test] fn test_dump_textproto_format_dedup() { let input = parse_test_flags_as_input(); let input2 = parse_test_flags_as_input(); let bytes = dump_parsed_flags(vec![input, input2], DumpFormat::Textproto, &[], true).unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert_eq!( None, crate::test::first_significant_code_diff( crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim() ) ); } fn parse_test_flags_as_input() -> Input { let parsed_flags = crate::test::parse_test_flags(); let binary_proto = parsed_flags.write_to_bytes().unwrap(); let cursor = std::io::Cursor::new(binary_proto); let reader = Box::new(cursor); Input { source: "test.data".to_string(), reader } } #[test] fn test_modify_parsed_flags_based_on_mode_prod() { let parsed_flags = crate::test::parse_test_flags(); let p_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::Production) .unwrap(); assert_eq!(parsed_flags.parsed_flag.len(), p_parsed_flags.len()); for (i, item) in p_parsed_flags.iter().enumerate() { assert!(parsed_flags.parsed_flag[i].eq(item)); } } #[test] fn test_modify_parsed_flags_based_on_mode_exported() { let parsed_flags = crate::test::parse_test_flags(); let p_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap(); assert_eq!(3, p_parsed_flags.len()); for flag in p_parsed_flags.iter() { assert_eq!(ProtoFlagState::DISABLED, flag.state()); assert_eq!(ProtoFlagPermission::READ_WRITE, flag.permission()); assert!(!flag.is_fixed_read_only()); assert!(flag.is_exported()); } let mut parsed_flags = crate::test::parse_test_flags(); parsed_flags.parsed_flag.retain(|pf| !pf.is_exported()); let error = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::Exported).unwrap_err(); assert_eq!("exported library contains no exported flags", format!("{:?}", error)); } #[test] fn test_assign_flag_ids() { let parsed_flags = crate::test::parse_test_flags(); let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string(); let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap(); let expected_flag_ids = HashMap::from([ (String::from("disabled_rw"), 0_u16), (String::from("disabled_rw_exported"), 1_u16), (String::from("disabled_rw_in_other_namespace"), 2_u16), (String::from("enabled_fixed_ro"), 3_u16), (String::from("enabled_fixed_ro_exported"), 4_u16), (String::from("enabled_ro"), 5_u16), (String::from("enabled_ro_exported"), 6_u16), (String::from("enabled_rw"), 7_u16), ]); assert_eq!(flag_ids, expected_flag_ids); } #[test] fn test_modify_parsed_flags_based_on_mode_force_read_only() { let parsed_flags = crate::test::parse_test_flags(); let p_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::ForceReadOnly) .unwrap(); assert_eq!(6, p_parsed_flags.len()); for pf in p_parsed_flags { assert_eq!(ProtoFlagPermission::READ_ONLY, pf.permission()); } let mut parsed_flags = crate::test::parse_test_flags(); parsed_flags.parsed_flag.retain_mut(|pf| pf.is_exported()); let error = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::ForceReadOnly) .unwrap_err(); assert_eq!( "force-read-only library contains no force-read-only flags", format!("{:?}", error) ); } } ================================================ FILE: tools/aconfig/aconfig/src/dump.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use aconfig_protos::{ ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint, }; use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags}; use anyhow::{anyhow, bail, Context, Result}; use protobuf::Message; #[derive(Clone, Debug, PartialEq, Eq)] pub enum DumpFormat { Protobuf, Textproto, Custom(String), } impl TryFrom<&str> for DumpFormat { type Error = anyhow::Error; fn try_from(value: &str) -> std::result::Result { match value { // protobuf formats "protobuf" => Ok(Self::Protobuf), "textproto" => Ok(Self::Textproto), // custom format _ => Ok(Self::Custom(value.to_owned())), } } } pub fn dump_parsed_flags(parsed_flags_iter: I, format: DumpFormat) -> Result> where I: Iterator, { let mut output = Vec::new(); match format { DumpFormat::Protobuf => { let parsed_flags = ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() }; parsed_flags.write_to_vec(&mut output)?; } DumpFormat::Textproto => { let parsed_flags = ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() }; let s = protobuf::text_format::print_to_string_pretty(&parsed_flags); output.extend_from_slice(s.as_bytes()); } DumpFormat::Custom(format) => { for flag in parsed_flags_iter { dump_custom_format(&flag, &format, &mut output); } } } Ok(output) } fn dump_custom_format(flag: &ProtoParsedFlag, format: &str, output: &mut Vec) { fn format_trace(trace: &[ProtoTracepoint]) -> String { trace .iter() .map(|tracepoint| { format!( "{}: {:?} + {:?}", tracepoint.source(), tracepoint.permission(), tracepoint.state() ) }) .collect::>() .join(", ") } fn format_trace_paths(trace: &[ProtoTracepoint]) -> String { trace.iter().map(|tracepoint| tracepoint.source()).collect::>().join(", ") } fn format_metadata(metadata: &ProtoFlagMetadata) -> String { format!("{:?}", metadata.purpose()) } let mut str = format // ProtoParsedFlag fields .replace("{package}", flag.package()) .replace("{name}", flag.name()) .replace("{namespace}", flag.namespace()) .replace("{description}", flag.description()) .replace("{bug}", &flag.bug.join(", ")) .replace("{state}", &format!("{:?}", flag.state())) .replace("{state:bool}", &format!("{}", flag.state() == ProtoFlagState::ENABLED)) .replace("{permission}", &format!("{:?}", flag.permission())) .replace("{trace}", &format_trace(&flag.trace)) .replace("{trace:paths}", &format_trace_paths(&flag.trace)) .replace("{is_fixed_read_only}", &format!("{}", flag.is_fixed_read_only())) .replace("{is_exported}", &format!("{}", flag.is_exported())) .replace("{container}", flag.container()) .replace("{metadata}", &format_metadata(&flag.metadata)) // ParsedFlagExt functions .replace("{fully_qualified_name}", &flag.fully_qualified_name()); str.push('\n'); output.extend_from_slice(str.as_bytes()); } pub type DumpPredicate = dyn Fn(&ProtoParsedFlag) -> bool; pub fn create_filter_predicate(filter: &str) -> Result> { let predicates = filter .split('+') .map(|sub_filter| create_filter_predicate_single(sub_filter)) .collect::>>()?; Ok(Box::new(move |flag| predicates.iter().all(|p| p(flag)))) } fn create_filter_predicate_single(filter: &str) -> Result> { fn enum_from_str(expected: &[T], s: &str) -> Result where T: std::fmt::Debug + Copy, { for candidate in expected.iter() { if s == format!("{:?}", candidate) { return Ok(*candidate); } } let expected = expected.iter().map(|state| format!("{:?}", state)).collect::>().join(", "); bail!("\"{s}\": not a valid flag state, expected one of {expected}"); } let error_msg = format!("\"{filter}\": filter syntax error"); let (what, arg) = filter.split_once(':').ok_or_else(|| anyhow!(error_msg.clone()))?; match what { "package" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.package() == expected)) } "name" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.name() == expected)) } "namespace" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.namespace() == expected)) } // description: not supported yet "bug" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.bug.join(", ") == expected)) } "state" => { let expected = enum_from_str(&[ProtoFlagState::ENABLED, ProtoFlagState::DISABLED], arg) .context(error_msg)?; Ok(Box::new(move |flag: &ProtoParsedFlag| flag.state() == expected)) } "permission" => { let expected = enum_from_str( &[ProtoFlagPermission::READ_ONLY, ProtoFlagPermission::READ_WRITE], arg, ) .context(error_msg)?; Ok(Box::new(move |flag: &ProtoParsedFlag| flag.permission() == expected)) } // trace: not supported yet "is_fixed_read_only" => { let expected: bool = arg.parse().context(error_msg)?; Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_fixed_read_only() == expected)) } "is_exported" => { let expected: bool = arg.parse().context(error_msg)?; Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_exported() == expected)) } "container" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.container() == expected)) } // metadata: not supported yet "fully_qualified_name" => { let expected = arg.to_owned(); Ok(Box::new(move |flag: &ProtoParsedFlag| flag.fully_qualified_name() == expected)) } _ => Err(anyhow!(error_msg)), } } #[cfg(test)] mod tests { use super::*; use crate::test::parse_test_flags; use aconfig_protos::ProtoParsedFlags; use protobuf::Message; fn parse_enabled_ro_flag() -> ProtoParsedFlag { parse_test_flags().parsed_flag.into_iter().find(|pf| pf.name() == "enabled_ro").unwrap() } #[test] fn test_dumpformat_from_str() { // supported format types assert_eq!(DumpFormat::try_from("protobuf").unwrap(), DumpFormat::Protobuf); assert_eq!(DumpFormat::try_from("textproto").unwrap(), DumpFormat::Textproto); assert_eq!( DumpFormat::try_from("foobar").unwrap(), DumpFormat::Custom("foobar".to_owned()) ); } #[test] fn test_dump_parsed_flags_protobuf_format() { let expected = protobuf::text_format::parse_from_str::( crate::test::TEST_FLAGS_TEXTPROTO, ) .unwrap() .write_to_bytes() .unwrap(); let parsed_flags = parse_test_flags(); let actual = dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Protobuf).unwrap(); assert_eq!(expected, actual); } #[test] fn test_dump_parsed_flags_textproto_format() { let parsed_flags = parse_test_flags(); let bytes = dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Textproto).unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim()); } #[test] fn test_dump_parsed_flags_custom_format() { macro_rules! assert_dump_parsed_flags_custom_format_contains { ($format:expr, $expected:expr) => { let parsed_flags = parse_test_flags(); let bytes = dump_parsed_flags( parsed_flags.parsed_flag.into_iter(), $format.try_into().unwrap(), ) .unwrap(); let text = std::str::from_utf8(&bytes).unwrap(); assert!(text.contains($expected)); }; } // custom format assert_dump_parsed_flags_custom_format_contains!( "{fully_qualified_name}={permission} + {state}", "com.android.aconfig.test.enabled_ro=READ_ONLY + ENABLED" ); } #[test] fn test_dump_custom_format() { macro_rules! assert_custom_format { ($format:expr, $expected:expr) => { let flag = parse_enabled_ro_flag(); let mut bytes = vec![]; dump_custom_format(&flag, $format, &mut bytes); let text = std::str::from_utf8(&bytes).unwrap(); assert_eq!(text, $expected); }; } assert_custom_format!("{package}", "com.android.aconfig.test\n"); assert_custom_format!("{name}", "enabled_ro\n"); assert_custom_format!("{namespace}", "aconfig_test\n"); assert_custom_format!("{description}", "This flag is ENABLED + READ_ONLY\n"); assert_custom_format!("{bug}", "abc\n"); assert_custom_format!("{state}", "ENABLED\n"); assert_custom_format!("{state:bool}", "true\n"); assert_custom_format!("{permission}", "READ_ONLY\n"); assert_custom_format!("{trace}", "tests/test.aconfig: READ_WRITE + DISABLED, tests/first.values: READ_WRITE + DISABLED, tests/second.values: READ_ONLY + ENABLED\n"); assert_custom_format!( "{trace:paths}", "tests/test.aconfig, tests/first.values, tests/second.values\n" ); assert_custom_format!("{is_fixed_read_only}", "false\n"); assert_custom_format!("{is_exported}", "false\n"); assert_custom_format!("{container}", "system\n"); assert_custom_format!("{metadata}", "PURPOSE_BUGFIX\n"); assert_custom_format!("name={name}|state={state}", "name=enabled_ro|state=ENABLED\n"); assert_custom_format!("{state}{state}{state}", "ENABLEDENABLEDENABLED\n"); } #[test] fn test_create_filter_predicate() { macro_rules! assert_create_filter_predicate { ($filter:expr, $expected:expr) => { let parsed_flags = parse_test_flags(); let predicate = create_filter_predicate($filter).unwrap(); let mut filtered_flags: Vec = parsed_flags .parsed_flag .into_iter() .filter(predicate) .map(|flag| flag.fully_qualified_name()) .collect(); filtered_flags.sort(); assert_eq!(&filtered_flags, $expected); }; } assert_create_filter_predicate!( "package:com.android.aconfig.test", &[ "com.android.aconfig.test.disabled_ro", "com.android.aconfig.test.disabled_rw", "com.android.aconfig.test.disabled_rw_exported", "com.android.aconfig.test.disabled_rw_in_other_namespace", "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", "com.android.aconfig.test.enabled_rw", ] ); assert_create_filter_predicate!( "name:disabled_rw", &["com.android.aconfig.test.disabled_rw"] ); assert_create_filter_predicate!( "namespace:other_namespace", &["com.android.aconfig.test.disabled_rw_in_other_namespace"] ); // description: not supported yet assert_create_filter_predicate!("bug:123", &["com.android.aconfig.test.disabled_ro",]); assert_create_filter_predicate!( "state:ENABLED", &[ "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", "com.android.aconfig.test.enabled_rw", ] ); assert_create_filter_predicate!( "permission:READ_ONLY", &[ "com.android.aconfig.test.disabled_ro", "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", ] ); // trace: not supported yet assert_create_filter_predicate!( "is_fixed_read_only:true", &[ "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", ] ); assert_create_filter_predicate!( "is_exported:true", &[ "com.android.aconfig.test.disabled_rw_exported", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro_exported", ] ); assert_create_filter_predicate!( "container:system", &[ "com.android.aconfig.test.disabled_ro", "com.android.aconfig.test.disabled_rw", "com.android.aconfig.test.disabled_rw_exported", "com.android.aconfig.test.disabled_rw_in_other_namespace", "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", "com.android.aconfig.test.enabled_rw", ] ); // metadata: not supported yet // synthesized fields assert_create_filter_predicate!( "fully_qualified_name:com.android.aconfig.test.disabled_rw", &["com.android.aconfig.test.disabled_rw"] ); // multiple sub filters assert_create_filter_predicate!( "permission:READ_ONLY+state:ENABLED", &[ "com.android.aconfig.test.enabled_fixed_ro", "com.android.aconfig.test.enabled_fixed_ro_exported", "com.android.aconfig.test.enabled_ro", "com.android.aconfig.test.enabled_ro_exported", ] ); } } ================================================ FILE: tools/aconfig/aconfig/src/main.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig` is a build time tool to manage build time configurations, such as feature flags. use aconfig_storage_file::DEFAULT_FILE_VERSION; use aconfig_storage_file::MAX_SUPPORTED_FILE_VERSION; use anyhow::{anyhow, bail, Context, Result}; use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command}; use core::any::Any; use std::fs; use std::io; use std::io::Write; use std::path::{Path, PathBuf}; mod codegen; mod commands; mod dump; mod storage; use aconfig_storage_file::StorageFileType; use codegen::CodegenMode; use convert_finalized_flags::FinalizedFlagMap; use dump::DumpFormat; #[cfg(test)] mod test; use commands::{Input, OutputFile}; const HELP_DUMP_CACHE: &str = r#" An aconfig cache file, created via `aconfig create-cache`. "#; const HELP_DUMP_FORMAT: &str = r#" Change the output format for each flag. The argument to --format is a format string. Each flag will be a copy of this string, with certain placeholders replaced by attributes of the flag. The placeholders are {package} {name} {namespace} {description} {bug} {state} {state:bool} {permission} {trace} {trace:paths} {is_fixed_read_only} {is_exported} {container} {metadata} {fully_qualified_name} Note: the format strings "textproto" and "protobuf" are handled in a special way: they output all flag attributes in text or binary protobuf format. Examples: # See which files were read to determine the value of a flag; the files were read in the order # listed. --format='{fully_qualified_name} {trace}' # Trace the files read for a specific flag. Useful during debugging. --filter=fully_qualified_name:com.foo.flag_name --format='{trace}' # Print a somewhat human readable description of each flag. --format='The flag {name} in package {package} is {state} and has permission {permission}.' "#; const HELP_DUMP_FILTER: &str = r#" Limit which flags to output. If --filter is omitted, all flags will be printed. If multiple --filter options are provided, the output will be limited to flags that match any of the filters. The argument to --filter is a search query. Multiple queries can be AND-ed together by concatenating them with a plus sign. Valid queries are: package: name: namespace: bug: state:ENABLED|DISABLED permission:READ_ONLY|READ_WRITE is_fixed_read_only:true|false is_exported:true|false container: fully_qualified_name: Note: there is currently no support for filtering based on these flag attributes: description, trace, metadata. Examples: # Print a single flag: --filter=fully_qualified_name:com.foo.flag_name # Print all known information about a single flag: --filter=fully_qualified_name:com.foo.flag_name --format=textproto # Print all flags in the com.foo package, and all enabled flags in the com.bar package: --filter=package:com.foo --filter=package.com.bar+state:ENABLED "#; const HELP_DUMP_DEDUP: &str = r#" Allow the same flag to be present in multiple cache files; if duplicates are found, collapse into a single instance. "#; fn cli() -> Command { Command::new("aconfig") .subcommand_required(true) .subcommand( Command::new("create-cache") .arg(Arg::new("package").long("package").required(true)) .arg(Arg::new("container").long("container").required(true)) .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append)) .arg(Arg::new("values").long("values").action(ArgAction::Append)) .arg( Arg::new("default-permission") .long("default-permission") .value_parser(aconfig_protos::flag_permission::parse_from_str) .default_value(aconfig_protos::flag_permission::to_string( &commands::DEFAULT_FLAG_PERMISSION, )), ) .arg( Arg::new("allow-read-write") .long("allow-read-write") .value_parser(clap::value_parser!(bool)) .default_value("true"), ) .arg(Arg::new("cache").long("cache").required(true)), ) .subcommand( Command::new("create-java-lib") .arg(Arg::new("cache").long("cache").required(true)) .arg(Arg::new("out").long("out").required(true)) .arg( Arg::new("mode") .long("mode") .value_parser(EnumValueParser::::new()) .default_value("production"), ) .arg( Arg::new("single-exported-file") .long("single-exported-file") .value_parser(clap::value_parser!(bool)) .default_value("false"), ) // TODO: b/395899938 - clean up flags for switching to new storage .arg( Arg::new("allow-instrumentation") .long("allow-instrumentation") .value_parser(clap::value_parser!(bool)) .default_value("false"), ) // TODO: b/395899938 - clean up flags for switching to new storage .arg( Arg::new("new-exported") .long("new-exported") .value_parser(clap::value_parser!(bool)) .default_value("false"), ) // Allows build flag toggling of checking API level in exported // flag lib for finalized API flags. // TODO: b/378936061 - Remove once build flag for API level // check is fully enabled. .arg( Arg::new("check-api-level") .long("check-api-level") .value_parser(clap::value_parser!(bool)) .default_value("false"), ), ) .subcommand( Command::new("create-cpp-lib") .arg(Arg::new("cache").long("cache").required(true)) .arg(Arg::new("out").long("out").required(true)) .arg( Arg::new("mode") .long("mode") .value_parser(EnumValueParser::::new()) .default_value("production"), ) .arg( Arg::new("allow-instrumentation") .long("allow-instrumentation") .value_parser(clap::value_parser!(bool)) .default_value("false"), ), ) .subcommand( Command::new("create-rust-lib") .arg(Arg::new("cache").long("cache").required(true)) .arg(Arg::new("out").long("out").required(true)) .arg( Arg::new("allow-instrumentation") .long("allow-instrumentation") .value_parser(clap::value_parser!(bool)) .default_value("false"), ) .arg( Arg::new("mode") .long("mode") .value_parser(EnumValueParser::::new()) .default_value("production"), ), ) .subcommand( Command::new("create-device-config-defaults") .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true)) .arg(Arg::new("out").long("out").default_value("-")), ) .subcommand( Command::new("create-device-config-sysprops") .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true)) .arg(Arg::new("out").long("out").default_value("-")), ) .subcommand( Command::new("dump-cache") .alias("dump") .arg( Arg::new("cache") .long("cache") .action(ArgAction::Append) .long_help(HELP_DUMP_CACHE.trim()), ) .arg( Arg::new("format") .long("format") .value_parser(|s: &str| DumpFormat::try_from(s)) .default_value( "{fully_qualified_name} [{container}]: {permission} + {state}", ) .long_help(HELP_DUMP_FORMAT.trim()), ) .arg( Arg::new("filter") .long("filter") .action(ArgAction::Append) .long_help(HELP_DUMP_FILTER.trim()), ) .arg( Arg::new("dedup") .long("dedup") .num_args(0) .action(ArgAction::SetTrue) .long_help(HELP_DUMP_DEDUP.trim()), ) .arg(Arg::new("out").long("out").default_value("-")), ) .subcommand( Command::new("create-storage") .arg( Arg::new("container") .long("container") .required(true) .help("The target container for the generated storage file."), ) .arg( Arg::new("file") .long("file") .value_parser(|s: &str| StorageFileType::try_from(s)), ) .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true)) .arg(Arg::new("out").long("out").required(true)) .arg( Arg::new("version") .long("version") .required(false) .value_parser(|s: &str| s.parse::()), ), ) } fn get_required_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Result<&'a T> where T: Any + Clone + Send + Sync + 'static, { matches .get_one::(arg_name) .ok_or(anyhow!("internal error: required argument '{}' not found", arg_name)) } fn get_optional_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Option<&'a T> where T: Any + Clone + Send + Sync + 'static, { matches.get_one::(arg_name) } fn open_zero_or_more_files(matches: &ArgMatches, arg_name: &str) -> Result> { let mut opened_files = vec![]; for path in matches.get_many::(arg_name).unwrap_or_default() { let file = Box::new(fs::File::open(path)?); opened_files.push(Input { source: path.to_string(), reader: file }); } Ok(opened_files) } fn open_single_file(matches: &ArgMatches, arg_name: &str) -> Result { let Some(path) = matches.get_one::(arg_name) else { bail!("missing argument {}", arg_name); }; let file = Box::new(fs::File::open(path)?); Ok(Input { source: path.to_string(), reader: file }) } fn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> { let path = root.join(&output_file.path); let parent = path .parent() .ok_or(anyhow!("unable to locate parent of output file {}", path.display()))?; fs::create_dir_all(parent) .with_context(|| format!("failed to create directory {}", parent.display()))?; let mut file = fs::File::create(&path).with_context(|| format!("failed to open {}", path.display()))?; file.write_all(&output_file.contents) .with_context(|| format!("failed to write to {}", path.display()))?; Ok(()) } fn write_output_to_file_or_stdout(path: &str, data: &[u8]) -> Result<()> { if path == "-" { io::stdout().write_all(data).context("failed to write to stdout")?; } else { fs::File::create(path) .with_context(|| format!("failed to open {}", path))? .write_all(data) .with_context(|| format!("failed to write to {}", path))?; } Ok(()) } fn load_finalized_flags() -> Result { let json_str = include_str!(concat!(env!("OUT_DIR"), "/finalized_flags_record.json")); let map = serde_json::from_str(json_str)?; Ok(map) } fn main() -> Result<()> { let matches = cli().get_matches(); match matches.subcommand() { Some(("create-cache", sub_matches)) => { let package = get_required_arg::(sub_matches, "package")?; let container = get_optional_arg::(sub_matches, "container").map(|c| c.as_str()); let declarations = open_zero_or_more_files(sub_matches, "declarations")?; let values = open_zero_or_more_files(sub_matches, "values")?; let default_permission = get_required_arg::( sub_matches, "default-permission", )?; let allow_read_write = get_optional_arg::(sub_matches, "allow-read-write") .expect("failed to parse allow-read-write"); let output = commands::parse_flags( package, container, declarations, values, *default_permission, *allow_read_write, ) .context("failed to create cache")?; let path = get_required_arg::(sub_matches, "cache")?; write_output_to_file_or_stdout(path, &output)?; } Some(("create-java-lib", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let mode = get_required_arg::(sub_matches, "mode")?; let allow_instrumentation = get_required_arg::(sub_matches, "allow-instrumentation")?; let new_exported = get_required_arg::(sub_matches, "new-exported")?; let single_exported_file = get_required_arg::(sub_matches, "single-exported-file")?; let check_api_level = get_required_arg::(sub_matches, "check-api-level")?; let finalized_flags: FinalizedFlagMap = if *check_api_level { load_finalized_flags()? } else { FinalizedFlagMap::new() }; let generated_files = commands::create_java_lib( cache, *mode, *allow_instrumentation, *new_exported, *single_exported_file, finalized_flags, ) .context("failed to create java lib")?; let dir = PathBuf::from(get_required_arg::(sub_matches, "out")?); generated_files .iter() .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?; } Some(("create-cpp-lib", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let mode = get_required_arg::(sub_matches, "mode")?; let generated_files = commands::create_cpp_lib(cache, *mode).context("failed to create cpp lib")?; let dir = PathBuf::from(get_required_arg::(sub_matches, "out")?); generated_files .iter() .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?; } Some(("create-rust-lib", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let mode = get_required_arg::(sub_matches, "mode")?; let generated_file = commands::create_rust_lib(cache, *mode).context("failed to create rust lib")?; let dir = PathBuf::from(get_required_arg::(sub_matches, "out")?); write_output_file_realtive_to_dir(&dir, &generated_file)?; } Some(("create-device-config-defaults", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let output = commands::create_device_config_defaults(cache) .context("failed to create device config defaults")?; let path = get_required_arg::(sub_matches, "out")?; write_output_to_file_or_stdout(path, &output)?; } Some(("create-device-config-sysprops", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let output = commands::create_device_config_sysprops(cache) .context("failed to create device config sysprops")?; let path = get_required_arg::(sub_matches, "out")?; write_output_to_file_or_stdout(path, &output)?; } Some(("dump-cache", sub_matches)) => { let input = open_zero_or_more_files(sub_matches, "cache")?; let format = get_required_arg::(sub_matches, "format") .context("failed to dump previously parsed flags")?; let filters = sub_matches .get_many::("filter") .unwrap_or_default() .map(String::as_ref) .collect::>(); let dedup = get_required_arg::(sub_matches, "dedup")?; let output = commands::dump_parsed_flags(input, format.clone(), &filters, *dedup)?; let path = get_required_arg::(sub_matches, "out")?; write_output_to_file_or_stdout(path, &output)?; } Some(("create-storage", sub_matches)) => { let version = get_optional_arg::(sub_matches, "version").unwrap_or(&DEFAULT_FILE_VERSION); if *version > MAX_SUPPORTED_FILE_VERSION { bail!("Invalid version selected ({})", version); } let file = get_required_arg::(sub_matches, "file") .context("Invalid storage file selection")?; let cache = open_zero_or_more_files(sub_matches, "cache")?; let container = get_required_arg::(sub_matches, "container")?; let path = get_required_arg::(sub_matches, "out")?; let output = commands::create_storage(cache, container, file, *version) .context("failed to create storage files")?; write_output_to_file_or_stdout(path, &output)?; } _ => unreachable!(), } Ok(()) } ================================================ FILE: tools/aconfig/aconfig/src/storage/flag_info.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState}; use aconfig_storage_file::{FlagInfoHeader, FlagInfoList, FlagInfoNode, StorageFileType}; use anyhow::{anyhow, Result}; fn new_header(container: &str, num_flags: u32, version: u32) -> FlagInfoHeader { FlagInfoHeader { version, container: String::from(container), file_type: StorageFileType::FlagInfo as u8, file_size: 0, num_flags, boolean_flag_offset: 0, } } pub fn create_flag_info( container: &str, packages: &[FlagPackage], version: u32, ) -> Result { // Exclude system/vendor/product flags that are RO+disabled. let mut filtered_packages = packages.to_vec(); if container == "system" || container == "vendor" || container == "product" { for package in filtered_packages.iter_mut() { package.boolean_flags.retain(|b| { !(b.state == Some(ProtoFlagState::DISABLED.into()) && b.permission == Some(ProtoFlagPermission::READ_ONLY.into())) }); } } let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum(); let mut is_flag_rw = vec![false; num_flags as usize]; for pkg in filtered_packages { let start_index = pkg.boolean_start_index as usize; let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?; for pf in pkg.boolean_flags { let fid = flag_ids .get(pf.name()) .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?; is_flag_rw[start_index + (*fid as usize)] = pf.permission() == ProtoFlagPermission::READ_WRITE; } } let mut list = FlagInfoList { header: new_header(container, num_flags, version), nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(), }; // initialize all header fields list.header.boolean_flag_offset = list.header.into_bytes().len() as u32; let bytes_per_node = FlagInfoNode::create(false).into_bytes().len() as u32; list.header.file_size = list.header.boolean_flag_offset + num_flags * bytes_per_node; Ok(list) } #[cfg(test)] mod tests { use super::*; use crate::storage::{group_flags_by_package, tests::parse_all_test_flags}; use aconfig_storage_file::DEFAULT_FILE_VERSION; pub fn create_test_flag_info_list_from_source() -> Result { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION); create_flag_info("mockup", &packages, DEFAULT_FILE_VERSION) } #[test] // this test point locks down the flag info creation and each field fn test_list_contents() { let flag_info_list = create_test_flag_info_list_from_source(); assert!(flag_info_list.is_ok()); let expected_flag_info_list = aconfig_storage_file::test_utils::create_test_flag_info_list(DEFAULT_FILE_VERSION); assert_eq!(flag_info_list.unwrap(), expected_flag_info_list); } } ================================================ FILE: tools/aconfig/aconfig/src/storage/flag_table.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::commands::{assign_flag_ids, should_include_flag}; use crate::storage::FlagPackage; use aconfig_protos::ProtoFlagPermission; use aconfig_storage_file::{ get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, StoredFlagType, }; use anyhow::{anyhow, Result}; fn new_header(container: &str, num_flags: u32, version: u32) -> FlagTableHeader { FlagTableHeader { version, container: String::from(container), file_type: StorageFileType::FlagMap as u8, file_size: 0, num_flags, bucket_offset: 0, node_offset: 0, } } // a struct that contains FlagTableNode and a bunch of other information to help // flag table creation #[derive(PartialEq, Debug, Clone)] struct FlagTableNodeWrapper { pub node: FlagTableNode, pub bucket_index: u32, } impl FlagTableNodeWrapper { fn new( package_id: u32, flag_name: &str, flag_type: StoredFlagType, flag_index: u16, num_buckets: u32, ) -> Self { let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets); let node = FlagTableNode { package_id, flag_name: flag_name.to_string(), flag_type, flag_index, next_offset: None, }; Self { node, bucket_index } } fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result> { // Exclude system/vendor/product flags that are RO+disabled. let mut filtered_package = package.clone(); filtered_package.boolean_flags.retain(|pf| should_include_flag(pf)); let flag_ids = assign_flag_ids(package.package_name, filtered_package.boolean_flags.iter().copied())?; filtered_package .boolean_flags .iter() .map(|&pf| { let fid = flag_ids .get(pf.name()) .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?; let flag_type = if pf.is_fixed_read_only() { StoredFlagType::FixedReadOnlyBoolean } else { match pf.permission() { ProtoFlagPermission::READ_WRITE => StoredFlagType::ReadWriteBoolean, ProtoFlagPermission::READ_ONLY => StoredFlagType::ReadOnlyBoolean, } }; Ok(Self::new(package.package_id, pf.name(), flag_type, *fid, num_buckets)) }) .collect::>>() } } pub fn create_flag_table( container: &str, packages: &[FlagPackage], version: u32, ) -> Result { // create table let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum(); let num_buckets = get_table_size(num_flags)?; let mut header = new_header(container, num_flags, version); let mut buckets = vec![None; num_buckets as usize]; let mut node_wrappers = packages .iter() .map(|pkg| FlagTableNodeWrapper::create_nodes(pkg, num_buckets)) .collect::>>()? .concat(); // initialize all header fields header.bucket_offset = header.into_bytes().len() as u32; header.node_offset = header.bucket_offset + num_buckets * 4; header.file_size = header.node_offset + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::() as u32; // sort nodes by bucket index for efficiency node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index)); // fill all node offset let mut offset = header.node_offset; for i in 0..node_wrappers.len() { let node_bucket_idx = node_wrappers[i].bucket_index; let next_node_bucket_idx = if i + 1 < node_wrappers.len() { Some(node_wrappers[i + 1].bucket_index) } else { None }; if buckets[node_bucket_idx as usize].is_none() { buckets[node_bucket_idx as usize] = Some(offset); } offset += node_wrappers[i].node.into_bytes().len() as u32; if let Some(index) = next_node_bucket_idx { if index == node_bucket_idx { node_wrappers[i].node.next_offset = Some(offset); } } } let table = FlagTable { header, buckets, nodes: node_wrappers.into_iter().map(|nw| nw.node).collect() }; Ok(table) } #[cfg(test)] mod tests { use aconfig_storage_file::DEFAULT_FILE_VERSION; use super::*; use crate::storage::{group_flags_by_package, tests::parse_all_test_flags}; fn create_test_flag_table_from_source() -> Result { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION); create_flag_table("mockup", &packages, DEFAULT_FILE_VERSION) } #[test] // this test point locks down the table creation and each field fn test_table_contents() { let flag_table = create_test_flag_table_from_source(); assert!(flag_table.is_ok()); let expected_flag_table = aconfig_storage_file::test_utils::create_test_flag_table(DEFAULT_FILE_VERSION); assert_eq!(flag_table.unwrap(), expected_flag_table); } } ================================================ FILE: tools/aconfig/aconfig/src/storage/flag_value.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState}; use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType}; use anyhow::{anyhow, Result}; fn new_header(container: &str, num_flags: u32, version: u32) -> FlagValueHeader { FlagValueHeader { version, container: String::from(container), file_type: StorageFileType::FlagVal as u8, file_size: 0, num_flags, boolean_value_offset: 0, } } pub fn create_flag_value( container: &str, packages: &[FlagPackage], version: u32, ) -> Result { // Exclude system/vendor/product flags that are RO+disabled. let mut filtered_packages = packages.to_vec(); if container == "system" || container == "vendor" || container == "product" { for package in filtered_packages.iter_mut() { package.boolean_flags.retain(|b| { !(b.state == Some(ProtoFlagState::DISABLED.into()) && b.permission == Some(ProtoFlagPermission::READ_ONLY.into())) }); } } let num_flags = filtered_packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum(); let mut list = FlagValueList { header: new_header(container, num_flags, version), booleans: vec![false; num_flags as usize], }; for pkg in filtered_packages { let start_index = pkg.boolean_start_index as usize; let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?; for pf in pkg.boolean_flags.iter() { let fid = flag_ids .get(pf.name()) .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?; list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED; } } // initialize all header fields list.header.boolean_value_offset = list.header.into_bytes().len() as u32; list.header.file_size = list.header.boolean_value_offset + num_flags; Ok(list) } #[cfg(test)] mod tests { use aconfig_storage_file::DEFAULT_FILE_VERSION; use super::*; use crate::storage::{group_flags_by_package, tests::parse_all_test_flags}; pub fn create_test_flag_value_list_from_source() -> Result { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION); create_flag_value("mockup", &packages, DEFAULT_FILE_VERSION) } #[test] // this test point locks down the flag value creation and each field fn test_list_contents() { let flag_value_list = create_test_flag_value_list_from_source(); assert!(flag_value_list.is_ok()); let expected_flag_value_list = aconfig_storage_file::test_utils::create_test_flag_value_list(DEFAULT_FILE_VERSION); assert_eq!(flag_value_list.unwrap(), expected_flag_value_list); } } ================================================ FILE: tools/aconfig/aconfig/src/storage/mod.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pub mod flag_info; pub mod flag_table; pub mod flag_value; pub mod package_table; use anyhow::Result; use std::collections::{HashMap, HashSet}; use crate::commands::compute_flags_fingerprint; use crate::storage::{ flag_info::create_flag_info, flag_table::create_flag_table, flag_value::create_flag_value, package_table::create_package_table, }; use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags}; use aconfig_storage_file::StorageFileType; #[derive(Clone)] pub struct FlagPackage<'a> { pub package_name: &'a str, pub package_id: u32, pub fingerprint: u64, pub flag_names: HashSet<&'a str>, pub boolean_flags: Vec<&'a ProtoParsedFlag>, // The index of the first boolean flag in this aconfig package among all boolean // flags in this container. pub boolean_start_index: u32, } impl<'a> FlagPackage<'a> { fn new(package_name: &'a str, package_id: u32) -> Self { FlagPackage { package_name, package_id, fingerprint: 0, flag_names: HashSet::new(), boolean_flags: vec![], boolean_start_index: 0, } } fn insert(&mut self, pf: &'a ProtoParsedFlag) { if self.flag_names.insert(pf.name()) { self.boolean_flags.push(pf); } } } pub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I, version: u32) -> Vec> where I: Iterator, { // group flags by package let mut packages: Vec> = Vec::new(); let mut package_index: HashMap<&str, usize> = HashMap::new(); for parsed_flags in parsed_flags_vec_iter { for parsed_flag in parsed_flags.parsed_flag.iter() { let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len())); if index == packages.len() { packages.push(FlagPackage::new(parsed_flag.package(), index as u32)); } // Exclude system/vendor/product flags that are RO+disabled. if (parsed_flag.container == Some("system".to_string()) || parsed_flag.container == Some("vendor".to_string()) || parsed_flag.container == Some("product".to_string())) && parsed_flag.permission == Some(ProtoFlagPermission::READ_ONLY.into()) && parsed_flag.state == Some(ProtoFlagState::DISABLED.into()) { continue; } packages[index].insert(parsed_flag); } } // Calculate boolean flag start index for each package let mut boolean_start_index = 0; for p in packages.iter_mut() { p.boolean_start_index = boolean_start_index; boolean_start_index += p.boolean_flags.len() as u32; if version >= 2 { let mut flag_names_vec = p.flag_names.clone().into_iter().map(String::from).collect::>(); let fingerprint = compute_flags_fingerprint(&mut flag_names_vec); p.fingerprint = fingerprint; } } packages } pub fn generate_storage_file<'a, I>( container: &str, parsed_flags_vec_iter: I, file: &StorageFileType, version: u32, ) -> Result> where I: Iterator, { let packages = group_flags_by_package(parsed_flags_vec_iter, version); match file { StorageFileType::PackageMap => { let package_table = create_package_table(container, &packages, version)?; Ok(package_table.into_bytes()) } StorageFileType::FlagMap => { let flag_table = create_flag_table(container, &packages, version)?; Ok(flag_table.into_bytes()) } StorageFileType::FlagVal => { let flag_value = create_flag_value(container, &packages, version)?; Ok(flag_value.into_bytes()) } StorageFileType::FlagInfo => { let flag_info = create_flag_info(container, &packages, version)?; Ok(flag_info.into_bytes()) } } } #[cfg(test)] mod tests { use aconfig_storage_file::DEFAULT_FILE_VERSION; use super::*; use crate::Input; pub fn parse_all_test_flags() -> Vec { let aconfig_files = [ ( "com.android.aconfig.storage.test_1", "storage_test_1.aconfig", include_bytes!("../../tests/storage_test_1.aconfig").as_slice(), "storage_test_1.value", include_bytes!("../../tests/storage_test_1.values").as_slice(), ), ( "com.android.aconfig.storage.test_2", "storage_test_2.aconfig", include_bytes!("../../tests/storage_test_2.aconfig").as_slice(), "storage_test_2.value", include_bytes!("../../tests/storage_test_2.values").as_slice(), ), ( "com.android.aconfig.storage.test_4", "storage_test_4.aconfig", include_bytes!("../../tests/storage_test_4.aconfig").as_slice(), "storage_test_4.value", include_bytes!("../../tests/storage_test_4.values").as_slice(), ), ]; aconfig_files .into_iter() .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| { let bytes = crate::commands::parse_flags( pkg, Some("system"), vec![Input { source: format!("tests/{}", aconfig_file).to_string(), reader: Box::new(aconfig_content), }], vec![Input { source: format!("tests/{}", value_file).to_string(), reader: Box::new(value_content), }], crate::commands::DEFAULT_FLAG_PERMISSION, true, ) .unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() }) .collect() } #[test] fn test_flag_package() { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), DEFAULT_FILE_VERSION); for pkg in packages.iter() { let pkg_name = pkg.package_name; assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len()); for pf in pkg.boolean_flags.iter() { assert!(pkg.flag_names.contains(pf.name())); assert_eq!(pf.package(), pkg_name); } } assert_eq!(packages.len(), 3); assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1"); assert_eq!(packages[0].package_id, 0); assert_eq!(packages[0].flag_names.len(), 3); assert!(packages[0].flag_names.contains("enabled_rw")); assert!(packages[0].flag_names.contains("disabled_rw")); assert!(packages[0].flag_names.contains("enabled_ro")); assert_eq!(packages[0].boolean_start_index, 0); assert_eq!(packages[0].fingerprint, 0); assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2"); assert_eq!(packages[1].package_id, 1); assert_eq!(packages[1].flag_names.len(), 3); assert!(packages[1].flag_names.contains("enabled_ro")); assert!(packages[1].flag_names.contains("disabled_rw")); assert!(packages[1].flag_names.contains("enabled_fixed_ro")); assert_eq!(packages[1].boolean_start_index, 3); assert_eq!(packages[0].fingerprint, 0); assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4"); assert_eq!(packages[2].package_id, 2); assert_eq!(packages[2].flag_names.len(), 2); assert!(packages[2].flag_names.contains("enabled_rw")); assert!(packages[2].flag_names.contains("enabled_fixed_ro")); assert_eq!(packages[2].boolean_start_index, 6); assert_eq!(packages[2].fingerprint, 0); } #[test] fn test_flag_package_with_fingerprint() { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), 2); for pkg in packages.iter() { let pkg_name = pkg.package_name; assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len()); for pf in pkg.boolean_flags.iter() { assert!(pkg.flag_names.contains(pf.name())); assert_eq!(pf.package(), pkg_name); } } assert_eq!(packages.len(), 3); assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1"); assert_eq!(packages[0].package_id, 0); assert_eq!(packages[0].flag_names.len(), 3); assert!(packages[0].flag_names.contains("enabled_rw")); assert!(packages[0].flag_names.contains("disabled_rw")); assert!(packages[0].flag_names.contains("enabled_ro")); assert_eq!(packages[0].boolean_start_index, 0); assert_eq!(packages[0].fingerprint, 15248948510590158086u64); assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2"); assert_eq!(packages[1].package_id, 1); assert_eq!(packages[1].flag_names.len(), 3); assert!(packages[1].flag_names.contains("enabled_ro")); assert!(packages[1].flag_names.contains("disabled_rw")); assert!(packages[1].flag_names.contains("enabled_fixed_ro")); assert_eq!(packages[1].boolean_start_index, 3); assert_eq!(packages[1].fingerprint, 4431940502274857964u64); assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4"); assert_eq!(packages[2].package_id, 2); assert_eq!(packages[2].flag_names.len(), 2); assert!(packages[2].flag_names.contains("enabled_rw")); assert!(packages[2].flag_names.contains("enabled_fixed_ro")); assert_eq!(packages[2].boolean_start_index, 6); assert_eq!(packages[2].fingerprint, 16233229917711622375u64); } } ================================================ FILE: tools/aconfig/aconfig/src/storage/package_table.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::Result; use aconfig_storage_file::{ get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType, }; use crate::storage::FlagPackage; fn new_header(container: &str, num_packages: u32, version: u32) -> PackageTableHeader { PackageTableHeader { version, container: String::from(container), file_type: StorageFileType::PackageMap as u8, file_size: 0, num_packages, bucket_offset: 0, node_offset: 0, } } // a struct that contains PackageTableNode and a bunch of other information to help // package table creation #[derive(PartialEq, Debug)] struct PackageTableNodeWrapper { pub node: PackageTableNode, pub bucket_index: u32, } impl PackageTableNodeWrapper { fn new(package: &FlagPackage, num_buckets: u32) -> Self { let node = PackageTableNode { package_name: String::from(package.package_name), package_id: package.package_id, fingerprint: package.fingerprint, boolean_start_index: package.boolean_start_index, next_offset: None, }; let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets); Self { node, bucket_index } } } pub fn create_package_table( container: &str, packages: &[FlagPackage], version: u32, ) -> Result { // create table let num_packages = packages.len() as u32; let num_buckets = get_table_size(num_packages)?; let mut header = new_header(container, num_packages, version); let mut buckets = vec![None; num_buckets as usize]; let mut node_wrappers: Vec<_> = packages .iter() .map(|pkg: &FlagPackage<'_>| PackageTableNodeWrapper::new(pkg, num_buckets)) .collect(); // initialize all header fields header.bucket_offset = header.into_bytes().len() as u32; header.node_offset = header.bucket_offset + num_buckets * 4; header.file_size = header.node_offset + node_wrappers.iter().map(|x| x.node.into_bytes(version).len()).sum::() as u32; // sort node_wrappers by bucket index for efficiency node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index)); // fill all node offset let mut offset = header.node_offset; for i in 0..node_wrappers.len() { let node_bucket_idx = node_wrappers[i].bucket_index; let next_node_bucket_idx = if i + 1 < node_wrappers.len() { Some(node_wrappers[i + 1].bucket_index) } else { None }; if buckets[node_bucket_idx as usize].is_none() { buckets[node_bucket_idx as usize] = Some(offset); } offset += node_wrappers[i].node.into_bytes(version).len() as u32; if let Some(index) = next_node_bucket_idx { if index == node_bucket_idx { node_wrappers[i].node.next_offset = Some(offset); } } } let table = PackageTable { header, buckets, nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(), }; Ok(table) } #[cfg(test)] mod tests { use aconfig_storage_file::{DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION}; use super::*; use crate::storage::{group_flags_by_package, tests::parse_all_test_flags}; pub fn create_test_package_table_from_source(version: u32) -> Result { let caches = parse_all_test_flags(); let packages = group_flags_by_package(caches.iter(), version); create_package_table("mockup", &packages, version) } #[test] // this test point locks down the table creation and each field fn test_table_contents_default_version() { let package_table_result = create_test_package_table_from_source(DEFAULT_FILE_VERSION); assert!(package_table_result.is_ok()); let package_table = package_table_result.unwrap(); let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table(DEFAULT_FILE_VERSION); assert_eq!(package_table.header, expected_package_table.header); assert_eq!(package_table.buckets, expected_package_table.buckets); for (node, expected_node) in package_table.nodes.iter().zip(expected_package_table.nodes.iter()) { assert_eq!(node.package_name, expected_node.package_name); assert_eq!(node.package_id, expected_node.package_id); assert_eq!(node.boolean_start_index, expected_node.boolean_start_index); assert_eq!(node.next_offset, expected_node.next_offset); } } #[test] // this test point locks down the table creation and each field fn test_table_contents_max_version() { let package_table_result = create_test_package_table_from_source(MAX_SUPPORTED_FILE_VERSION); assert!(package_table_result.is_ok()); let package_table = package_table_result.unwrap(); let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table(MAX_SUPPORTED_FILE_VERSION); assert_eq!(package_table.header, expected_package_table.header); assert_eq!(package_table.buckets, expected_package_table.buckets); for (node, expected_node) in package_table.nodes.iter().zip(expected_package_table.nodes.iter()) { assert_eq!(node.package_name, expected_node.package_name); assert_eq!(node.package_id, expected_node.package_id); assert_eq!(node.boolean_start_index, expected_node.boolean_start_index); assert_eq!(node.next_offset, expected_node.next_offset); } } } ================================================ FILE: tools/aconfig/aconfig/src/test.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #[cfg(test)] pub use test_utils::*; #[cfg(test)] pub mod test_utils { use crate::commands::Input; use aconfig_protos::ProtoParsedFlags; use itertools; pub const TEST_PACKAGE: &str = "com.android.aconfig.test"; pub const TEST_FLAGS_TEXTPROTO: &str = r#" parsed_flag { package: "com.android.aconfig.test" name: "disabled_ro" namespace: "aconfig_test" description: "This flag is DISABLED + READ_ONLY" bug: "123" state: DISABLED permission: READ_ONLY trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: DISABLED permission: READ_ONLY } is_fixed_read_only: false is_exported: false container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "disabled_rw" namespace: "aconfig_test" description: "This flag is DISABLED + READ_WRITE" bug: "456" state: DISABLED permission: READ_WRITE trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } is_fixed_read_only: false is_exported: false container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "disabled_rw_exported" namespace: "aconfig_test" description: "This flag is DISABLED + READ_WRITE and exported" bug: "111" state: DISABLED permission: READ_WRITE trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: DISABLED permission: READ_WRITE } is_fixed_read_only: false is_exported: true container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "disabled_rw_in_other_namespace" namespace: "other_namespace" description: "This flag is DISABLED + READ_WRITE, and is defined in another namespace" bug: "999" state: DISABLED permission: READ_WRITE trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: DISABLED permission: READ_WRITE } is_fixed_read_only: false is_exported: false container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "enabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + ENABLED" bug: "" state: ENABLED permission: READ_ONLY trace { source: "tests/test.aconfig" state: DISABLED permission: READ_ONLY } trace { source: "tests/first.values" state: ENABLED permission: READ_ONLY } is_fixed_read_only: true is_exported: false container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "enabled_fixed_ro_exported" namespace: "aconfig_test" description: "This flag is fixed ENABLED + READ_ONLY and exported" bug: "111" state: ENABLED permission: READ_ONLY trace { source: "tests/test.aconfig" state: DISABLED permission: READ_ONLY } trace { source: "tests/first.values" state: ENABLED permission: READ_ONLY } is_fixed_read_only: true is_exported: true container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "enabled_ro" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" state: ENABLED permission: READ_ONLY trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: DISABLED permission: READ_WRITE } trace { source: "tests/second.values" state: ENABLED permission: READ_ONLY } is_fixed_read_only: false is_exported: false container: "system" metadata { purpose: PURPOSE_BUGFIX } } parsed_flag { package: "com.android.aconfig.test" name: "enabled_ro_exported" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY and exported" bug: "111" state: ENABLED permission: READ_ONLY trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: ENABLED permission: READ_ONLY } is_fixed_read_only: false is_exported: true container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } parsed_flag { package: "com.android.aconfig.test" name: "enabled_rw" namespace: "aconfig_test" description: "This flag is ENABLED + READ_WRITE" bug: "" state: ENABLED permission: READ_WRITE trace { source: "tests/test.aconfig" state: DISABLED permission: READ_WRITE } trace { source: "tests/first.values" state: ENABLED permission: READ_WRITE } is_fixed_read_only: false is_exported: false container: "system" metadata { purpose: PURPOSE_UNSPECIFIED } } "#; pub fn parse_read_only_test_flags() -> ProtoParsedFlags { let bytes = crate::commands::parse_flags( "com.android.aconfig.test", Some("system"), vec![Input { source: "tests/read_only_test.aconfig".to_string(), reader: Box::new(include_bytes!("../tests/read_only_test.aconfig").as_slice()), }], vec![Input { source: "tests/read_only_test.values".to_string(), reader: Box::new(include_bytes!("../tests/read_only_test.values").as_slice()), }], crate::commands::DEFAULT_FLAG_PERMISSION, true, ) .unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() } pub fn parse_test_flags() -> ProtoParsedFlags { let bytes = crate::commands::parse_flags( "com.android.aconfig.test", Some("system"), vec![Input { source: "tests/test.aconfig".to_string(), reader: Box::new(include_bytes!("../tests/test.aconfig").as_slice()), }], vec![ Input { source: "tests/first.values".to_string(), reader: Box::new(include_bytes!("../tests/first.values").as_slice()), }, Input { source: "tests/second.values".to_string(), reader: Box::new(include_bytes!("../tests/second.values").as_slice()), }, ], crate::commands::DEFAULT_FLAG_PERMISSION, true, ) .unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() } pub fn parse_second_package_flags() -> ProtoParsedFlags { let bytes = crate::commands::parse_flags( "com.android.aconfig.second_test", Some("system"), vec![Input { source: "tests/test_second_package.aconfig".to_string(), reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()), }], vec![Input { source: "tests/third.values".to_string(), reader: Box::new(include_bytes!("../tests/third.values").as_slice()), }], crate::commands::DEFAULT_FLAG_PERMISSION, true, ) .unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() } pub fn first_significant_code_diff(a: &str, b: &str) -> Option { let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); match itertools::diff_with(a, b, |left, right| left == right) { Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => { Some(format!("'{}' vs '{}'", left.next().unwrap(), right.next().unwrap())) } Some(itertools::Diff::Shorter(_, mut left)) => { Some(format!("LHS trailing data: '{}'", left.next().unwrap())) } Some(itertools::Diff::Longer(_, mut right)) => { Some(format!("RHS trailing data: '{}'", right.next().unwrap())) } None => None, } } /// Asserts that the two strings are equivalent. For use in tests. Fails /// with formatted error message for easier debugging. pub fn assert_no_significant_code_diff(expected: &str, actual: &str) { let expected = expected.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); let actual = actual.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); let fail_message: Option = match itertools::diff_with(expected, actual, |left, right| left == right) { Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => Some(format!( "DOES NOT MATCH: 1) expected, 2) actual:\n{}\n{}", left.next().unwrap(), right.next().unwrap() )), Some(itertools::Diff::Shorter(_, mut left)) => { Some(format!("LHS trailing data: '{}'", left.next().unwrap())) } Some(itertools::Diff::Longer(_, mut right)) => { Some(format!("RHS trailing data: '{}'", right.next().unwrap())) } None => None, }; assert!(fail_message.is_none(), "{}", fail_message.unwrap()); } #[test] fn test_first_significant_code_diff() { assert!(first_significant_code_diff("", "").is_none()); assert!(first_significant_code_diff(" a", "\n\na\n").is_none()); let a = r#" public class A { private static final String FOO = "FOO"; public static void main(String[] args) { System.out.println("FOO=" + FOO); } } "#; let b = r#" public class A { private static final String FOO = "BAR"; public static void main(String[] args) { System.out.println("foo=" + FOO); } } "#; assert_eq!(Some(r#"'private static final String FOO = "FOO";' vs 'private static final String FOO = "BAR";'"#.to_string()), first_significant_code_diff(a, b)); assert_eq!( Some("LHS trailing data: 'b'".to_string()), first_significant_code_diff("a\nb", "a") ); assert_eq!( Some("RHS trailing data: 'b'".to_string()), first_significant_code_diff("a", "a\nb") ); } } ================================================ FILE: tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template ================================================ package {package_name}; {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -endif }} import java.util.Arrays; {{ -if library_exported }} import java.util.HashMap; import java.util.Map; {{ -endif }} import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Predicate; {{ -if library_exported }} import android.os.Build; {{ -endif }} {{ -if single_exported_file }} {{ -if library_exported }} @Deprecated {#- PREFER ExportedFlags #} {{ -endif }} {{ -else }} /** @hide */ {{ -endif }} public class CustomFeatureFlags implements FeatureFlags \{ private BiPredicate> mGetValueImpl; public CustomFeatureFlags(BiPredicate> getValueImpl) \{ mGetValueImpl = getValueImpl; } {{ -for item in flag_elements}} @Override {{ if not library_exported }} @UnsupportedAppUsage{{ -endif }} public boolean {item.method_name}() \{ return getValue(Flags.FLAG_{item.flag_name_constant_suffix}, FeatureFlags::{item.method_name}); } {{ endfor }} {{ -if not library_exported }} public boolean isFlagReadOnlyOptimized(String flagName) \{ if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) \{ return true; } return false; } @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() \{ return false; } {{ -endif }} protected boolean getValue(String flagName, Predicate getter) \{ return mGetValueImpl.test(flagName, getter); } public List getFlagNames() \{ return Arrays.asList( {{ -for item in flag_elements }} Flags.FLAG_{item.flag_name_constant_suffix} {{ -if not @last }},{{ endif }} {{ -endfor }} ); } private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( {{ -for item in flag_elements }} {{ -if not item.is_read_write }} Flags.FLAG_{item.flag_name_constant_suffix}, {{ -endif }} {{ -endfor }} ""{# The empty string here is to resolve the ending comma #} ) ); {{ -if library_exported }} private Map mFinalizedFlags = new HashMap<>( Map.ofEntries( {{ -for item in flag_elements }} {{ -if item.finalized_sdk_present }} Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, {item.finalized_sdk_value}), {{ -endif }} {{ -endfor }} Map.entry("", Integer.MAX_VALUE){# The empty entry to avoid empty entries #} ) ); public boolean isFlagFinalized(String flagName) \{ if (!mFinalizedFlags.containsKey(flagName)) \{ return false; } return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName); } {{ -endif }} } ================================================ FILE: tools/aconfig/aconfig/templates/ExportedFlags.java.template ================================================ package {package_name}; {#- CODEGEN FOR EXPORTED MODE FOR NEW STORAGE SINGLE EXPORTED FILE#} import android.os.Build; import android.os.flagging.AconfigPackage; import android.util.Log; public final class ExportedFlags \{ {{ -for item in flag_elements}} public static final String FLAG_{item.flag_name_constant_suffix} = "{item.device_config_flag}"; {{- endfor }} private static final String TAG = "ExportedFlags"; private static volatile boolean isCached = false; {{ for flag in flag_elements }} private static boolean {flag.method_name} = false; {{ -endfor }} {#- end flag_elements #} private ExportedFlags() \{} private void init() \{ try \{ AconfigPackage reader = AconfigPackage.load("{package_name}"); {{ -for namespace_with_flags in namespace_flags }} {{ -for flag in namespace_with_flags.flags }} {flag.method_name} = reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value}); {{ -endfor }} {#- end namespace_with_flags.flags #} {{ -endfor }} {#- end namespace_flags #} } catch (Exception e) \{ // pass Log.e(TAG, e.toString()); } catch (LinkageError e) \{ // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.w(TAG, e.toString()); } isCached = true; } {{ -for flag in flag_elements }} public static boolean {flag.method_name}() \{ {{ -if flag.finalized_sdk_present }} if (Build.VERSION.SDK_INT >= {flag.finalized_sdk_value}) \{ return true; } {{ -endif}} {#- end finalized_sdk_present#} if (!featureFlags.isCached) \{ featureFlags.init(); } return featureFlags.{flag.method_name}; } {{ -endfor }} private static ExportedFlags featureFlags = new ExportedFlags(); } ================================================ FILE: tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template ================================================ package {package_name}; import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; {{ -if single_exported_file }} {{ -if library_exported }} @Deprecated {#- PREFER ExportedFlags #} {{ -endif }} {{ -else }} /** @hide */ {{ -endif }} public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{ private final Map mFlagMap = new HashMap<>(); private final FeatureFlags mDefaults; public FakeFeatureFlagsImpl() \{ this(null); } public FakeFeatureFlagsImpl(FeatureFlags defaults) \{ super(null); mDefaults = defaults; // Initialize the map with null values for (String flagName : getFlagNames()) \{ mFlagMap.put(flagName, null); } } @Override protected boolean getValue(String flagName, Predicate getter) \{ Boolean value = this.mFlagMap.get(flagName); if (value != null) \{ return value; } if (mDefaults != null) \{ return getter.test(mDefaults); } throw new IllegalArgumentException(flagName + " is not set"); } public void setFlag(String flagName, boolean value) \{ if (!this.mFlagMap.containsKey(flagName)) \{ throw new IllegalArgumentException("no such flag " + flagName); } this.mFlagMap.put(flagName, value); } public void resetAll() \{ for (Map.Entry entry : mFlagMap.entrySet()) \{ entry.setValue(null); } } } ================================================ FILE: tools/aconfig/aconfig/templates/FeatureFlags.java.template ================================================ package {package_name}; {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -endif }} {{ -if single_exported_file }} {{ -if library_exported }} /** * @deprecated Use \{@link ExportedFlags} instead. */ @Deprecated {#- PREFER ExportedFlags #} {{ -endif }} {{ -else }} /** @hide */ {{ -endif }} public interface FeatureFlags \{ {{ for item in flag_elements }} {{ -if not item.is_read_write }} {{ -if item.default_value }} @com.android.aconfig.annotations.AssumeTrueForR8 {{ -else }} @com.android.aconfig.annotations.AssumeFalseForR8 {{ -endif- }} {{ -endif }} {{ -if not library_exported }} @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage {{ -endif }} boolean {item.method_name}(); {{ -endfor }} } ================================================ FILE: tools/aconfig/aconfig/templates/FeatureFlagsImpl.deviceConfig.java.template ================================================ package {package_name}; {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -endif }} {#- end of not library_exported#} {{ -if runtime_lookup_required }} import android.os.Binder; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; {{ -endif }} {#- end of runtime_lookup_required#} /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ -if runtime_lookup_required }} {{ -for namespace_with_flags in namespace_flags }} private static volatile boolean {namespace_with_flags.namespace}_is_cached = false; {{ -endfor- }} {{ for flag in flag_elements }} {{- if flag.is_read_write }} private static boolean {flag.method_name} = {flag.default_value}; {{ -endif }} {#- end of is_read_write#} {{ -endfor }} {{ for namespace_with_flags in namespace_flags }} private void load_overrides_{namespace_with_flags.namespace}() \{ final long ident = Binder.clearCallingIdentity(); try \{ Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}"); {{ -for flag in namespace_with_flags.flags }} {{ -if flag.is_read_write }} {flag.method_name} = properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value}); {{ -endif }} {#- end of is_read_write#} {{ -endfor }} } catch (NullPointerException e) \{ throw new RuntimeException( "Cannot read value from namespace {namespace_with_flags.namespace} " + "from DeviceConfig. It could be that the code using flag " + "executed before SettingsProvider initialization. Please use " + "fixed read-only flag by adding is_fixed_read_only: true in " + "flag declaration.", e ); } catch (SecurityException e) \{ // for isolated process case, skip loading flag value from the storage, use the default } finally \{ Binder.restoreCallingIdentity(ident); } {namespace_with_flags.namespace}_is_cached = true; } {{ endfor- }} {{ -endif }}{#- end of runtime_lookup_required #} {{ -for flag in flag_elements }} @Override {{ -if not library_exported }} @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage {{ -endif }}{#- end of not library_exported #} public boolean {flag.method_name}() \{ {{ -if flag.is_read_write }} if (!{flag.device_config_namespace}_is_cached) \{ load_overrides_{flag.device_config_namespace}(); } return {flag.method_name}; {{ -else }} {#- else is_read_write #} return {flag.default_value}; {{ -endif }}{#- end of is_read_write #} } {{ endfor }} } ================================================ FILE: tools/aconfig/aconfig/templates/FeatureFlagsImpl.exported.java.template ================================================ package {package_name}; {#- CODEGEN FOR EXPORTED MODE FOR NEW STORAGE #} import android.os.Build; import android.os.flagging.AconfigPackage; import android.util.Log; {{ -if single_exported_file }} {{ -if library_exported }} /** * @deprecated Use \{@link ExportedFlags} instead. */ @Deprecated {#- PREFER ExportedFlags #} {{ -endif }} {{ -else }} /** @hide */ {{ -endif }} public final class FeatureFlagsImpl implements FeatureFlags \{ private static final String TAG = "FeatureFlagsImplExport"; private static volatile boolean isCached = false; {{ for flag in flag_elements }} private static boolean {flag.method_name} = false; {{ -endfor }} {#- end flag_elements #} private void init() \{ try \{ AconfigPackage reader = AconfigPackage.load("{package_name}"); {{ -for namespace_with_flags in namespace_flags }} {{ -for flag in namespace_with_flags.flags }} {{ -if flag.finalized_sdk_present }} {flag.method_name} = Build.VERSION.SDK_INT >= {flag.finalized_sdk_value} ? true : reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value}); {{ - else }} {#- else finalized_sdk_present #} {flag.method_name} = reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value}); {{ -endif}} {#- end finalized_sdk_present#} {{ -endfor }} {#- end namespace_with_flags.flags #} {{ -endfor }} {#- end namespace_flags #} } catch (Exception e) \{ // pass Log.e(TAG, e.toString()); } catch (LinkageError e) \{ // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.w(TAG, e.toString()); } isCached = true; } {{ -for flag in flag_elements }} @Override public boolean {flag.method_name}() \{ if (!isCached) \{ init(); } return {flag.method_name}; } {{ endfor }} {#- end flag_elements #} } ================================================ FILE: tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template ================================================ package {package_name}; {#- CODEGEN FOR INTERNAL MODE FOR NEW STORAGE #} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -if runtime_lookup_required }} {{ if is_platform_container }} import android.os.flagging.PlatformAconfigPackageInternal; {{ -else }} {#- else is_platform_container #} import android.os.flagging.AconfigPackageInternal; {{ -endif }} {#- end of is_platform_container#} import android.util.Log; {{ -endif }} {#- end of runtime_lookup_required#} /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ -if runtime_lookup_required }} private static final String TAG = "FeatureFlagsImpl"; private static volatile boolean isCached = false; {{ for flag in flag_elements }} {{ -if flag.is_read_write }} private static boolean {flag.method_name} = {flag.default_value}; {{ -endif }} {#- end of is_read_write#} {{ -endfor }} {#- else flag_elements #} private void init() \{ try \{ {{ if is_platform_container }} PlatformAconfigPackageInternal reader = PlatformAconfigPackageInternal.load("{package_name}", {package_fingerprint}); {{ -else }} {#- else is_platform_container #} AconfigPackageInternal reader = AconfigPackageInternal.load("{package_name}", {package_fingerprint}); {{ -endif }} {#- end of is_platform_container#} {{ -for namespace_with_flags in namespace_flags }} {{ -for flag in namespace_with_flags.flags }} {{ -if flag.is_read_write }} {flag.method_name} = reader.getBooleanFlagValue({flag.flag_offset}); {{ -endif }} {#- is_read_write#} {{ -endfor }} {#- else namespace_with_flags.flags #} {{ -endfor }} {#- else namespace_flags #} } catch (Exception e) \{ Log.e(TAG, e.toString()); } catch (LinkageError e) \{ // for mainline module running on older devices. // This should be replaces to version check, after the version bump. Log.e(TAG, e.toString()); } isCached = true; } {{ -endif }}{#- end of runtime_lookup_required #} {{ -for flag in flag_elements }} @Override @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage public boolean {flag.method_name}() \{ {{ -if flag.is_read_write }} if (!isCached) \{ init(); } return {flag.method_name}; {{ -else }}{#- else is_read_write #} return {flag.default_value}; {{ -endif }} {#- end of is_read_write#} } {{ endfor }} {#- else flag_elements #} } ================================================ FILE: tools/aconfig/aconfig/templates/FeatureFlagsImpl.test_mode.java.template ================================================ package {package_name}; {#- CODEGEN FOR TEST MODE #} /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ for flag in flag_elements }} @Override {{ -if not library_exported }} @com.android.aconfig.annotations.AconfigFlagAccessor {{ -endif }} public boolean {flag.method_name}() \{ throw new UnsupportedOperationException( "Method is not implemented."); } {{ endfor- }} } ================================================ FILE: tools/aconfig/aconfig/templates/Flags.java.template ================================================ package {package_name}; {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ else }} import android.os.Build; {{ -endif }} {#- end not library_exported#} {{ -if single_exported_file }} {{ -if library_exported }} /** * @deprecated Use \{@link ExportedFlags} instead. */ @Deprecated {#- PREFER ExportedFlags #} {{ -endif }} {{ -else }} /** @hide */ {{ -endif }} public final class Flags \{ {{ -for item in flag_elements}} /** @hide */ public static final String FLAG_{item.flag_name_constant_suffix} = "{item.device_config_flag}"; {{- endfor }} {{ -for item in flag_elements}} {{ -if not item.is_read_write }} {{ -if item.default_value }} @com.android.aconfig.annotations.AssumeTrueForR8 {{ -else }} @com.android.aconfig.annotations.AssumeFalseForR8 {{ -endif }} {{ -endif }} {{ -if not library_exported }} @com.android.aconfig.annotations.AconfigFlagAccessor @UnsupportedAppUsage {{ -endif }} public static boolean {item.method_name}() \{ {{ if library_exported- }} {{ -if item.finalized_sdk_present }} if (Build.VERSION.SDK_INT >= {item.finalized_sdk_value}) \{ return true; } {{ -endif}} {#- end finalized_sdk_present#} {{ -endif}} {#- end library_exported#} return FEATURE_FLAGS.{item.method_name}(); } {{ -endfor }} {{ -if is_test_mode }} public static void setFeatureFlags(FeatureFlags featureFlags) \{ Flags.FEATURE_FLAGS = featureFlags; } public static void unsetFeatureFlags() \{ Flags.FEATURE_FLAGS = null; } {{ -endif }} private static FeatureFlags FEATURE_FLAGS{{ -if not is_test_mode }} = new FeatureFlagsImpl(){{ -endif- }}; } ================================================ FILE: tools/aconfig/aconfig/templates/cpp_exported_header.template ================================================ #pragma once {{ if not is_test_mode- }} {{ if has_fixed_read_only- }} #ifndef {package_macro} #define {package_macro}(FLAG) {package_macro}_##FLAG #endif {{ for item in class_elements }} {{ -if item.is_fixed_read_only }} #ifndef {package_macro}_{item.flag_macro} #define {package_macro}_{item.flag_macro} {item.default_value} #endif {{ -endif }} {{ -endfor }} {{ -endif }} {{ -endif }} #ifdef __cplusplus #include namespace {cpp_namespace} \{ class flag_provider_interface \{ public: virtual ~flag_provider_interface() = default; {{ -for item in class_elements}} virtual bool {item.flag_name}() = 0; {{ -endfor }} {{ -if is_test_mode }} {{ -for item in class_elements}} virtual void {item.flag_name}(bool val) = 0; {{ -endfor }} virtual void reset_flags() \{} {{ -endif }} }; extern std::unique_ptr provider_; {{ for item in class_elements}} {{ if not is_test_mode }}{{ if item.is_fixed_read_only }}constexpr {{ endif }}{{ endif -}} inline bool {item.flag_name}() \{ {{ -if is_test_mode }} return provider_->{item.flag_name}(); {{ -else }} {{ -if item.readwrite }} return provider_->{item.flag_name}(); {{ -else }} {{ -if item.is_fixed_read_only }} return {package_macro}_{item.flag_macro}; {{ -else }} return {item.default_value}; {{ -endif }} {{ -endif }} {{ -endif }} } {{ -if is_test_mode }} inline void {item.flag_name}(bool val) \{ provider_->{item.flag_name}(val); } {{ -endif }} {{ -endfor }} {{ -if is_test_mode }} inline void reset_flags() \{ return provider_->reset_flags(); } {{ -endif }} } extern "C" \{ #endif // __cplusplus {{ for item in class_elements }} bool {header}_{item.flag_name}(); {{ -if is_test_mode }} void set_{header}_{item.flag_name}(bool val); {{ -endif }} {{ -endfor }} {{ -if is_test_mode }} void {header}_reset_flags(); {{ -endif }} #ifdef __cplusplus } // extern "C" #endif ================================================ FILE: tools/aconfig/aconfig/templates/cpp_source_file.template ================================================ #include "{header}.h" {{ if readwrite- }} #include #include "aconfig_storage/aconfig_storage_read_api.hpp" #include #define LOG_TAG "aconfig_cpp_codegen" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) {{ -endif }} {{ if is_test_mode }} #include #include {{ -else- }} {{ if readwrite- }} #include {{ -endif }} {{ -endif }} namespace {cpp_namespace} \{ {{ if is_test_mode }} class flag_provider : public flag_provider_interface \{ private: std::unordered_map overrides_; {{ if readwrite- }} uint32_t boolean_start_index_; std::unique_ptr flag_value_file_; bool package_exists_in_storage_; {{ -endif }} public: {{ if readwrite- }} flag_provider() : overrides_() , boolean_start_index_() , flag_value_file_(nullptr) , package_exists_in_storage_(true) \{ auto package_map_file = aconfig_storage::get_mapped_file( "{container}", aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) \{ ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); package_exists_in_storage_ = false; return; } auto context = aconfig_storage::get_package_read_context( **package_map_file, "{package}"); if (!context.ok()) \{ ALOGE("error: failed to get package read context: %s", context.error().c_str()); package_exists_in_storage_ = false; return; } if (!(context->package_exists)) \{ package_exists_in_storage_ = false; return; } // cache package boolean flag start index boolean_start_index_ = context->boolean_start_index; // unmap package map file and free memory delete *package_map_file; auto flag_value_file = aconfig_storage::get_mapped_file( "{container}", aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) \{ ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); package_exists_in_storage_ = false; return; } // cache flag value file flag_value_file_ = std::unique_ptr( *flag_value_file); } {{ -else }} flag_provider() : overrides_() \{} {{ -endif }} {{ for item in class_elements }} virtual bool {item.flag_name}() override \{ auto it = overrides_.find("{item.flag_name}"); if (it != overrides_.end()) \{ return it->second; } else \{ {{ if item.readwrite- }} if (!package_exists_in_storage_) \{ return {item.default_value}; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + {item.flag_offset}); if (!value.ok()) \{ ALOGE("error: failed to read flag value: %s", value.error().c_str()); return {item.default_value}; } else \{ return *value; } {{ -else }} return {item.default_value}; {{ -endif }} } } virtual void {item.flag_name}(bool val) override \{ overrides_["{item.flag_name}"] = val; } {{ endfor }} virtual void reset_flags() override \{ overrides_.clear(); } }; {{ -else- }} class flag_provider : public flag_provider_interface \{ public: {{ if readwrite- }} flag_provider() : cache_({readwrite_count}, -1) , boolean_start_index_() , flag_value_file_(nullptr) , package_exists_in_storage_(true) \{ auto package_map_file = aconfig_storage::get_mapped_file( "{container}", aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) \{ ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); package_exists_in_storage_ = false; return; } auto context = aconfig_storage::get_package_read_context( **package_map_file, "{package}"); if (!context.ok()) \{ ALOGE("error: failed to get package read context: %s", context.error().c_str()); package_exists_in_storage_ = false; return; } if (!(context->package_exists)) \{ package_exists_in_storage_ = false; return; } // cache package boolean flag start index boolean_start_index_ = context->boolean_start_index; // unmap package map file and free memory delete *package_map_file; auto flag_value_file = aconfig_storage::get_mapped_file( "{container}", aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) \{ ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); package_exists_in_storage_ = false; return; } // cache flag value file flag_value_file_ = std::unique_ptr( *flag_value_file); } {{ -endif }} {{ -for item in class_elements }} virtual bool {item.flag_name}() override \{ {{ -if item.readwrite }} if (cache_[{item.readwrite_idx}] == -1) \{ if (!package_exists_in_storage_) \{ return {item.default_value}; } auto value = aconfig_storage::get_boolean_flag_value( *flag_value_file_, boolean_start_index_ + {item.flag_offset}); if (!value.ok()) \{ ALOGE("error: failed to read flag value: %s", value.error().c_str()); return {item.default_value}; } cache_[{item.readwrite_idx}] = *value; } return cache_[{item.readwrite_idx}]; {{ -else }} {{ -if item.is_fixed_read_only }} return {package_macro}_{item.flag_macro}; {{ -else }} return {item.default_value}; {{ -endif }} {{ -endif }} } {{ -endfor }} {{ if readwrite- }} private: std::vector cache_ = std::vector({readwrite_count}, -1); uint32_t boolean_start_index_; std::unique_ptr flag_value_file_; bool package_exists_in_storage_; {{ -endif }} }; {{ -endif }} std::unique_ptr provider_ = std::make_unique(); } {{ for item in class_elements }} bool {header}_{item.flag_name}() \{ {{ -if is_test_mode }} return {cpp_namespace}::{item.flag_name}(); {{ -else }} {{ -if item.readwrite }} return {cpp_namespace}::{item.flag_name}(); {{ -else }} {{ -if item.is_fixed_read_only }} return {package_macro}_{item.flag_macro}; {{ -else }} return {item.default_value}; {{ -endif }} {{ -endif }} {{ -endif }} } {{ -if is_test_mode }} void set_{header}_{item.flag_name}(bool val) \{ {cpp_namespace}::{item.flag_name}(val); } {{ -endif }} {{ endfor }} {{ -if is_test_mode }} void {header}_reset_flags() \{ {cpp_namespace}::reset_flags(); } {{ -endif }} ================================================ FILE: tools/aconfig/aconfig/templates/rust.template ================================================ //! codegenerated rust flag lib use aconfig_storage_read_api::\{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context}; use std::path::Path; use std::io::Write; use std::sync::LazyLock; use log::\{log, LevelFilter, Level}; /// flag provider pub struct FlagProvider; {{ if has_readwrite- }} static PACKAGE_CONTEXT: LazyLock, AconfigStorageError>> = LazyLock::new(|| unsafe \{ get_mapped_storage_file("{container}", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "{package}")) }); static FLAG_VAL_MAP: LazyLock> = LazyLock::new(|| unsafe \{ get_mapped_storage_file("{container}", StorageFileType::FlagVal) }); {{ -for flag in template_flags }} {{ -if flag.readwrite }} /// flag value cache for {flag.name} static CACHED_{flag.name}: LazyLock = LazyLock::new(|| \{ // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); {{ -if use_package_fingerprint }} let fingerprint_check_failed: bool = PACKAGE_CONTEXT .as_ref() .is_ok_and(|package_context| \{ match package_context \{ Some(context) => \{ return context.fingerprint != {package_fingerprint} }, None => \{ log!(Level::Info, "aconfig_rust_codegen: missing fingerprint; performing lookup."); false } } }); if fingerprint_check_failed \{ log!(Level::Error, "Fingerprint mismatch for package {package}; returning flag default ({flag.default_value}) for {flag.name}."); return {flag.default_value}; } {{ -endif }} let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: \{err}")) .and_then(|flag_val_map| \{ PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: \{err}")) .and_then(|package_context| \{ match package_context \{ Some(context) => \{ get_boolean_flag_value(&flag_val_map, context.boolean_start_index + {flag.flag_offset}) .map_err(|err| format!("failed to get flag: \{err}")) }, None => \{ log!(Level::Error, "no context found for package {package}"); Err(format!("failed to flag package {package}")) } } }) }); match flag_value_result \{ Ok(flag_value) => \{ return flag_value; }, Err(err) => \{ log!(Level::Error, "aconfig_rust_codegen: error: \{err}"); return {flag.default_value}; } } }); {{ -endif }} {{ -endfor }} {{ -endif }} impl FlagProvider \{ {{ for flag in template_flags }} /// query flag {flag.name} pub fn {flag.name}(&self) -> bool \{ {{ -if flag.readwrite }} *CACHED_{flag.name} {{ -else }} {flag.default_value} {{ -endif }} } {{ endfor }} } /// flag provider pub static PROVIDER: FlagProvider = FlagProvider; {{ for flag in template_flags }} /// query flag {flag.name} #[inline(always)] pub fn {flag.name}() -> bool \{ {{ -if flag.readwrite }} PROVIDER.{flag.name}() {{ -else }} {flag.default_value} {{ -endif }} } {{ endfor }} ================================================ FILE: tools/aconfig/aconfig/templates/rust_test.template ================================================ //! codegenerated rust flag lib use aconfig_storage_read_api::\{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context}; use std::collections::BTreeMap; use std::path::Path; use std::io::Write; use std::sync::\{LazyLock, Mutex}; use log::\{log, LevelFilter, Level}; /// flag provider pub struct FlagProvider \{ overrides: BTreeMap<&'static str, bool>, } {{ if has_readwrite- }} static PACKAGE_CONTEXT: LazyLock, AconfigStorageError>> = LazyLock::new(|| unsafe \{ get_mapped_storage_file("{container}", StorageFileType::PackageMap) .and_then(|package_map| get_package_read_context(&package_map, "{package}")) }); static FLAG_VAL_MAP: LazyLock> = LazyLock::new(|| unsafe \{ get_mapped_storage_file("{container}", StorageFileType::FlagVal) }); {{ -for flag in template_flags }} {{ -if flag.readwrite }} /// flag value cache for {flag.name} static CACHED_{flag.name}: LazyLock = LazyLock::new(|| \{ // This will be called multiple times. Subsequent calls after the first are noops. logger::init( logger::Config::default() .with_tag_on_device("aconfig_rust_codegen") .with_max_level(LevelFilter::Info)); {{ -if use_package_fingerprint }} let fingerprint_check_failed: bool = PACKAGE_CONTEXT .as_ref() .is_ok_and(|package_context| \{ match package_context \{ Some(context) => \{ return context.fingerprint != {package_fingerprint} }, None => \{ log!(Level::Info, "aconfig_rust_codegen: missing fingerprint; performing lookup."); false } } }); if fingerprint_check_failed \{ log!(Level::Error, "Fingerprint mismatch for package {package}; returning flag default ({flag.default_value}) for {flag.name}."); return {flag.default_value}; } {{ -endif }} let flag_value_result = FLAG_VAL_MAP .as_ref() .map_err(|err| format!("failed to get flag val map: \{err}")) .and_then(|flag_val_map| \{ PACKAGE_CONTEXT .as_ref() .map_err(|err| format!("failed to get package read context: \{err}")) .and_then(|package_context| \{ match package_context \{ Some(context) => \{ get_boolean_flag_value(&flag_val_map, context.boolean_start_index + {flag.flag_offset}) .map_err(|err| format!("failed to get flag: \{err}")) }, None => \{ log!(Level::Error, "no context found for package {package}"); Err(format!("failed to flag package {package}")) } } }) }); match flag_value_result \{ Ok(flag_value) => \{ return flag_value; }, Err(err) => \{ log!(Level::Error, "aconfig_rust_codegen: error: \{err}"); return {flag.default_value}; } } }); {{ -endif }} {{ -endfor }} {{ -endif }} impl FlagProvider \{ {{ for flag in template_flags }} /// query flag {flag.name} pub fn {flag.name}(&self) -> bool \{ self.overrides.get("{flag.name}").copied().unwrap_or( {{ if flag.readwrite -}} *CACHED_{flag.name} {{ -else- }} {flag.default_value} {{ -endif }} ) } /// set flag {flag.name} pub fn set_{flag.name}(&mut self, val: bool) \{ self.overrides.insert("{flag.name}", val); } {{ endfor }} /// clear all flag overrides pub fn reset_flags(&mut self) \{ self.overrides.clear(); } } /// flag provider pub static PROVIDER: Mutex = Mutex::new( FlagProvider \{overrides: BTreeMap::new()} ); {{ for flag in template_flags }} /// query flag {flag.name} #[inline(always)] pub fn {flag.name}() -> bool \{ PROVIDER.lock().unwrap().{flag.name}() } /// set flag {flag.name} #[inline(always)] pub fn set_{flag.name}(val: bool) \{ PROVIDER.lock().unwrap().set_{flag.name}(val); } {{ endfor }} /// clear all flag override pub fn reset_flags() \{ PROVIDER.lock().unwrap().reset_flags() } ================================================ FILE: tools/aconfig/aconfig/tests/AconfigHostTest.java ================================================ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.android.aconfig.test.FakeFeatureFlagsImpl; import com.android.aconfig.test.FeatureFlags; import com.android.aconfig.test.FeatureFlagsImpl; import com.android.aconfig.test.Flags; @RunWith(JUnit4.class) public final class AconfigHostTest { @Test public void testThrowsExceptionIfFlagNotSet() { assertThrows(NullPointerException.class, () -> Flags.disabledRo()); FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); assertThrows(IllegalArgumentException.class, () -> featureFlags.disabledRo()); } @Test public void testSetFlagInFakeFeatureFlagsImpl() { FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); assertTrue(featureFlags.enabledRw()); featureFlags.setFlag(Flags.FLAG_ENABLED_RW, false); assertFalse(featureFlags.enabledRw()); //Set Flags assertThrows(NullPointerException.class, () -> Flags.enabledRw()); Flags.setFeatureFlags(featureFlags); featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); assertTrue(Flags.enabledRw()); Flags.unsetFeatureFlags(); } @Test public void testSetFlagWithRandomName() { FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); assertThrows(IllegalArgumentException.class, () -> featureFlags.setFlag("Randome_name", true)); } @Test public void testResetFlagsInFakeFeatureFlagsImpl() { FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); featureFlags.setFlag(Flags.FLAG_ENABLED_RO, true); assertTrue(featureFlags.enabledRo()); featureFlags.resetAll(); assertThrows(IllegalArgumentException.class, () -> featureFlags.enabledRo()); // Set value after reset featureFlags.setFlag(Flags.FLAG_ENABLED_RO, false); assertFalse(featureFlags.enabledRo()); } @Test public void testFlagsSetFeatureFlags() { FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); assertThrows(NullPointerException.class, () -> Flags.enabledRw()); Flags.setFeatureFlags(featureFlags); assertTrue(Flags.enabledRw()); Flags.unsetFeatureFlags(); } @Test public void testFlagsUnsetFeatureFlags() { FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl(); featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); assertThrows(NullPointerException.class, () -> Flags.enabledRw()); Flags.setFeatureFlags(featureFlags); assertTrue(Flags.enabledRw()); Flags.unsetFeatureFlags(); assertThrows(NullPointerException.class, () -> Flags.enabledRw()); } @Test public void testFeatureFlagsImplNotImpl() { FeatureFlags featureFlags = new FeatureFlagsImpl(); assertThrows(UnsupportedOperationException.class, () -> featureFlags.enabledRw()); } } ================================================ FILE: tools/aconfig/aconfig/tests/AconfigTest.java ================================================ import static com.android.aconfig.test.Flags.FLAG_DISABLED_RO; import static com.android.aconfig.test.Flags.FLAG_DISABLED_RW; import static com.android.aconfig.test.Flags.FLAG_ENABLED_FIXED_RO; import static com.android.aconfig.test.Flags.FLAG_ENABLED_RO; import static com.android.aconfig.test.Flags.FLAG_ENABLED_RW; import static com.android.aconfig.test.Flags.disabledRo; import static com.android.aconfig.test.Flags.disabledRw; import static com.android.aconfig.test.Flags.enabledFixedRo; import static com.android.aconfig.test.Flags.enabledRo; import static com.android.aconfig.test.Flags.enabledRw; import static com.android.aconfig.test.exported.Flags.exportedFlag; import static com.android.aconfig.test.exported.Flags.FLAG_EXPORTED_FLAG; import static com.android.aconfig.test.forcereadonly.Flags.froRw; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.android.aconfig.test.FakeFeatureFlagsImpl; import com.android.aconfig.test.FeatureFlags; @RunWith(JUnit4.class) public final class AconfigTest { @Test public void testDisabledReadOnlyFlag() { assertEquals("com.android.aconfig.test.disabled_ro", FLAG_DISABLED_RO); assertFalse(disabledRo()); } @Test public void testEnabledReadOnlyFlag() { assertEquals("com.android.aconfig.test.disabled_rw", FLAG_DISABLED_RW); assertTrue(enabledRo()); } @Test public void testEnabledFixedReadOnlyFlag() { assertEquals("com.android.aconfig.test.enabled_fixed_ro", FLAG_ENABLED_FIXED_RO); assertTrue(enabledFixedRo()); } @Test public void testDisabledReadWriteFlag() { assertEquals("com.android.aconfig.test.enabled_ro", FLAG_ENABLED_RO); assertFalse(disabledRw()); } @Test public void testEnabledReadWriteFlag() { assertEquals("com.android.aconfig.test.enabled_rw", FLAG_ENABLED_RW); assertTrue(enabledRw()); } @Test public void testFakeFeatureFlagsImplImpled() { FakeFeatureFlagsImpl fakeFeatureFlags = new FakeFeatureFlagsImpl(); fakeFeatureFlags.setFlag(FLAG_ENABLED_RW, false); assertFalse(fakeFeatureFlags.enabledRw()); } @Test public void testExportedFlag() { assertEquals("com.android.aconfig.test.exported.exported_flag", FLAG_EXPORTED_FLAG); assertFalse(exportedFlag()); } @Test public void testForceReadOnly() { assertFalse(froRw()); } } ================================================ FILE: tools/aconfig/aconfig/tests/AndroidManifest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "com_android_aconfig_test.h" #include "gtest/gtest.h" using namespace com::android::aconfig::test; TEST(AconfigTest, TestDisabledRwExportedFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_rw_exported()); ASSERT_FALSE(provider_->disabled_rw_exported()); ASSERT_FALSE(disabled_rw_exported()); } TEST(AconfigTest, TestEnabledFixedRoExportedFlag) { // TODO: change to assertTrue(enabledFixedRoExported()) when the build supports reading tests/*.values ASSERT_FALSE(com_android_aconfig_test_enabled_fixed_ro_exported()); ASSERT_FALSE(provider_->enabled_fixed_ro_exported()); ASSERT_FALSE(enabled_fixed_ro_exported()); } TEST(AconfigTest, TestEnabledRoExportedFlag) { // TODO: change to assertTrue(enabledRoExported()) when the build supports reading tests/*.values ASSERT_FALSE(com_android_aconfig_test_enabled_ro_exported()); ASSERT_FALSE(provider_->enabled_ro_exported()); ASSERT_FALSE(enabled_ro_exported()); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs ================================================ #[cfg(not(feature = "cargo"))] #[test] fn test_flags() { assert!(!aconfig_test_rust_library::disabled_rw_exported()); assert!(!aconfig_test_rust_library::enabled_fixed_ro_exported()); assert!(!aconfig_test_rust_library::enabled_ro_exported()); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "com_android_aconfig_test.h" #include "gtest/gtest.h" using namespace com::android::aconfig::test; TEST(AconfigTest, TestDisabledReadOnlyFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_ro()); ASSERT_FALSE(provider_->disabled_ro()); ASSERT_FALSE(disabled_ro()); } TEST(AconfigTest, TestEnabledReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_ro()); ASSERT_TRUE(provider_->enabled_ro()); ASSERT_TRUE(enabled_ro()); } TEST(AconfigTest, TestDisabledReadWriteFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_rw()); ASSERT_FALSE(provider_->disabled_rw()); ASSERT_FALSE(disabled_rw()); } TEST(AconfigTest, TestEnabledReadWriteFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_rw()); ASSERT_TRUE(provider_->enabled_rw()); ASSERT_TRUE(enabled_rw()); } TEST(AconfigTest, TestEnabledFixedReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro()); ASSERT_TRUE(provider_->enabled_fixed_ro()); ASSERT_TRUE(enabled_fixed_ro()); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs ================================================ #[cfg(not(feature = "cargo"))] #[test] fn test_flags() { assert!(!aconfig_test_rust_library::disabled_ro()); assert!(!aconfig_test_rust_library::disabled_rw()); assert!(!aconfig_test_rust_library::disabled_rw_in_other_namespace()); assert!(aconfig_test_rust_library::enabled_fixed_ro()); assert!(aconfig_test_rust_library::enabled_ro()); assert!(aconfig_test_rust_library::enabled_rw()); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs ================================================ #[cfg(not(feature = "cargo"))] #[test] fn test_flags() { assert!(!aconfig_test_rust_library::disabled_ro()); assert!(!aconfig_test_rust_library::disabled_rw()); assert!(aconfig_test_rust_library::enabled_ro()); assert!(aconfig_test_rust_library::enabled_rw()); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_test.cpp ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "com_android_aconfig_test.h" #include "gtest/gtest.h" using namespace com::android::aconfig::test; TEST(AconfigTest, TestDisabledReadOnlyFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_ro()); ASSERT_FALSE(provider_->disabled_ro()); ASSERT_FALSE(disabled_ro()); } TEST(AconfigTest, TestEnabledReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_ro()); ASSERT_TRUE(provider_->enabled_ro()); ASSERT_TRUE(enabled_ro()); } TEST(AconfigTest, TestDisabledReadWriteFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_rw()); ASSERT_FALSE(provider_->disabled_rw()); ASSERT_FALSE(disabled_rw()); } TEST(AconfigTest, TestEnabledReadWriteFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_rw()); ASSERT_TRUE(provider_->enabled_rw()); ASSERT_TRUE(enabled_rw()); } TEST(AconfigTest, TestEnabledFixedReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro()); ASSERT_TRUE(provider_->enabled_fixed_ro()); ASSERT_TRUE(enabled_fixed_ro()); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs ================================================ #[cfg(not(feature = "cargo"))] #[test] fn test_flags() { assert!(!aconfig_test_rust_library::disabled_ro()); assert!(!aconfig_test_rust_library::disabled_rw()); assert!(aconfig_test_rust_library::enabled_ro()); assert!(aconfig_test_rust_library::enabled_rw()); aconfig_test_rust_library::set_disabled_ro(true); assert!(aconfig_test_rust_library::disabled_ro()); aconfig_test_rust_library::set_disabled_rw(true); assert!(aconfig_test_rust_library::disabled_rw()); aconfig_test_rust_library::set_enabled_ro(false); assert!(!aconfig_test_rust_library::enabled_ro()); aconfig_test_rust_library::set_enabled_rw(false); assert!(!aconfig_test_rust_library::enabled_rw()); aconfig_test_rust_library::reset_flags(); assert!(!aconfig_test_rust_library::disabled_ro()); assert!(!aconfig_test_rust_library::disabled_rw()); assert!(aconfig_test_rust_library::enabled_ro()); assert!(aconfig_test_rust_library::enabled_rw()); } ================================================ FILE: tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "com_android_aconfig_test.h" #include "gtest/gtest.h" using namespace com::android::aconfig::test; class AconfigTest : public ::testing::Test { protected: void SetUp() override { reset_flags(); } }; TEST_F(AconfigTest, TestDisabledReadOnlyFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_ro()); ASSERT_FALSE(provider_->disabled_ro()); ASSERT_FALSE(disabled_ro()); } TEST_F(AconfigTest, TestEnabledReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_ro()); ASSERT_TRUE(provider_->enabled_ro()); ASSERT_TRUE(enabled_ro()); } TEST_F(AconfigTest, TestDisabledReadWriteFlag) { ASSERT_FALSE(com_android_aconfig_test_disabled_rw()); ASSERT_FALSE(provider_->disabled_rw()); ASSERT_FALSE(disabled_rw()); } TEST_F(AconfigTest, TestEnabledReadWriteFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_rw()); ASSERT_TRUE(provider_->enabled_rw()); ASSERT_TRUE(enabled_rw()); } TEST_F(AconfigTest, TestEnabledFixedReadOnlyFlag) { ASSERT_TRUE(com_android_aconfig_test_enabled_fixed_ro()); ASSERT_TRUE(provider_->enabled_fixed_ro()); ASSERT_TRUE(enabled_fixed_ro()); } TEST_F(AconfigTest, OverrideFlagValue) { ASSERT_FALSE(disabled_ro()); disabled_ro(true); ASSERT_TRUE(disabled_ro()); } TEST_F(AconfigTest, ResetFlagValue) { ASSERT_FALSE(disabled_ro()); ASSERT_TRUE(enabled_ro()); disabled_ro(true); enabled_ro(false); ASSERT_TRUE(disabled_ro()); ASSERT_FALSE(enabled_ro()); reset_flags(); ASSERT_FALSE(disabled_ro()); ASSERT_TRUE(enabled_ro()); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: tools/aconfig/aconfig/tests/first.values ================================================ flag_value { package: "com.android.aconfig.test" name: "disabled_ro" state: DISABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.test" name: "enabled_ro" state: DISABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.test" name: "enabled_rw" state: ENABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.test" name: "disabled_rw_in_other_namespace" state: DISABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.test" name: "enabled_fixed_ro" state: ENABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.test" name: "enabled_ro_exported" state: ENABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.test" name: "disabled_rw_exported" state: DISABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.test" name: "enabled_fixed_ro_exported" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/read_only_test.aconfig ================================================ package: "com.android.aconfig.test" container: "system" flag { name: "enabled_ro" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" } flag { name: "disabled_ro" namespace: "aconfig_test" description: "This flag is DISABLED + READ_ONLY" bug: "123" } flag { name: "enabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + ENABLED" bug: "" is_fixed_read_only: true } flag { name: "disabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + DISABLED" bug: "" is_fixed_read_only: true } ================================================ FILE: tools/aconfig/aconfig/tests/read_only_test.values ================================================ flag_value { package: "com.android.aconfig.test" name: "disabled_ro" state: DISABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.test" name: "enabled_ro" state: ENABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.test" name: "enabled_fixed_ro" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/second.values ================================================ flag_value { package: "com.android.aconfig.test" name: "enabled_ro" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_1.aconfig ================================================ package: "com.android.aconfig.storage.test_1" container: "system" flag { name: "enabled_rw" namespace: "aconfig_test" description: "This flag is ENABLED + READ_WRITE" bug: "" } flag { name: "disabled_rw" namespace: "aconfig_test" description: "This flag is DISABLED + READ_WRITE" bug: "456" is_exported: true } flag { name: "enabled_ro" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_1.values ================================================ flag_value { package: "com.android.aconfig.storage.test_1" name: "enabled_rw" state: ENABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.storage.test_1" name: "disabled_rw" state: DISABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.storage.test_1" name: "enabled_ro" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_2.aconfig ================================================ package: "com.android.aconfig.storage.test_2" container: "system" flag { name: "enabled_ro" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" } flag { name: "disabled_rw" namespace: "aconfig_test" description: "This flag is DISABLED + READ_ONLY" bug: "123" } flag { name: "enabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + ENABLED" bug: "" is_fixed_read_only: true } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_2.values ================================================ flag_value { package: "com.android.aconfig.storage.test_2" name: "enabled_ro" state: ENABLED permission: READ_ONLY } flag_value { package: "com.android.aconfig.storage.test_2" name: "disabled_rw" state: DISABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.storage.test_2" name: "enabled_fixed_ro" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_4.aconfig ================================================ package: "com.android.aconfig.storage.test_4" container: "system" flag { name: "enabled_rw" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" } flag { name: "enabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + ENABLED" bug: "" is_fixed_read_only: true } ================================================ FILE: tools/aconfig/aconfig/tests/storage_test_4.values ================================================ flag_value { package: "com.android.aconfig.storage.test_4" name: "enabled_rw" state: ENABLED permission: READ_WRITE } flag_value { package: "com.android.aconfig.storage.test_4" name: "enabled_fixed_ro" state: ENABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/aconfig/tests/test.aconfig ================================================ package: "com.android.aconfig.test" container: "system" # This flag's final value is calculated from: # - test.aconfig: DISABLED + READ_WRITE (default) # - first.values: DISABLED + READ_WRITE # - second.values: ENABLED + READ_ONLY flag { name: "enabled_ro" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY" bug: "abc" metadata { purpose: PURPOSE_BUGFIX } } # This flag's final value is calculated from: # - test.aconfig: DISABLED + READ_WRITE (default) # - first.values: ENABLED + READ_WRITE flag { name: "enabled_rw" namespace: "aconfig_test" description: "This flag is ENABLED + READ_WRITE" # for bug fields, the empty string is a discouraged but valid value bug: "" } # This flag's final value is calculated from: # - test.aconfig: DISABLED + READ_WRITE (default) # - first.values: DISABLED + READ_ONLY flag { name: "disabled_ro" namespace: "aconfig_test" description: "This flag is DISABLED + READ_ONLY" bug: "123" } # This flag's final value is calculated from: # - test.aconfig: DISABLED + READ_WRITE (default) flag { name: "disabled_rw" namespace: "aconfig_test" description: "This flag is DISABLED + READ_WRITE" bug: "456" } # This flag's final value calculated from: # - test.aconfig: DISABLED + READ_ONLY # - first.values: ENABLED + READ_ONLY flag { name: "enabled_fixed_ro" namespace: "aconfig_test" description: "This flag is fixed READ_ONLY + ENABLED" bug: "" is_fixed_read_only: true } flag { name: "disabled_rw_in_other_namespace" namespace: "other_namespace" description: "This flag is DISABLED + READ_WRITE, and is defined in another namespace" bug: "999" } flag { name: "enabled_ro_exported" namespace: "aconfig_test" description: "This flag is ENABLED + READ_ONLY and exported" bug: "111" is_exported: true } flag { name: "disabled_rw_exported" namespace: "aconfig_test" description: "This flag is DISABLED + READ_WRITE and exported" bug: "111" is_exported: true } flag { name: "enabled_fixed_ro_exported" namespace: "aconfig_test" description: "This flag is fixed ENABLED + READ_ONLY and exported" bug: "111" is_fixed_read_only: true is_exported: true } ================================================ FILE: tools/aconfig/aconfig/tests/test_exported.aconfig ================================================ package: "com.android.aconfig.test.exported" container: "system" flag { name: "exported_flag" namespace: "aconfig_test" description: "This is an exported flag" is_exported: true bug: "888" } flag { name: "not_exported_flag" namespace: "aconfig_test" description: "This flag is not exported" bug: "777" } ================================================ FILE: tools/aconfig/aconfig/tests/test_force_read_only.aconfig ================================================ package: "com.android.aconfig.test.forcereadonly" container: "system" flag { name: "fro_exported" namespace: "aconfig_test" description: "This is an exported flag" is_exported: true bug: "888" } flag { name: "fro_rw" namespace: "aconfig_test" description: "This flag is not exported" bug: "777" } ================================================ FILE: tools/aconfig/aconfig/tests/test_second_package.aconfig ================================================ package: "com.android.aconfig.second_test" container: "system" flag { name: "testing_flag" namespace: "another_namespace" description: "This is a flag for testing." bug: "123" } ================================================ FILE: tools/aconfig/aconfig/tests/third.values ================================================ flag_value { package: "com.android.aconfig.second_test" name: "testing_flag" state: DISABLED permission: READ_WRITE } ================================================ FILE: tools/aconfig/aconfig_device_paths/Android.bp ================================================ // Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "libaconfig_device_paths.defaults", edition: "2021", clippy_lints: "android", lints: "android", srcs: ["src/lib.rs"], rustlibs: [ "libaconfig_protos", "libanyhow", "libprotobuf", ], } rust_library { name: "libaconfig_device_paths", crate_name: "aconfig_device_paths", host_supported: true, defaults: ["libaconfig_device_paths.defaults"], apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } genrule { name: "libaconfig_java_device_paths_src", srcs: ["src/DeviceProtosTemplate.java"], out: ["DeviceProtos.java"], tool_files: ["partition_aconfig_flags_paths.txt"], cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)", } java_library { name: "aconfig_device_paths_java", srcs: [":libaconfig_java_device_paths_src"], static_libs: [ "libaconfig_java_proto_nano", ], sdk_version: "core_platform", apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } genrule { name: "libaconfig_java_host_device_paths_src", srcs: ["src/HostDeviceProtosTemplate.java"], out: ["HostDeviceProtos.java"], tool_files: [ "partition_aconfig_flags_paths.txt", "mainline_aconfig_flags_paths.txt", ], cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out).tmp && " + "sed -e '/MAINLINE_T/{r$(location mainline_aconfig_flags_paths.txt)' -e 'd}' $(out).tmp > $(out)", } java_library_host { name: "aconfig_host_device_paths_java", srcs: [":libaconfig_java_host_device_paths_src"], } genrule { name: "java_device_paths_test_util_src", srcs: ["src/DeviceProtosTestUtilTemplate.java"], out: ["DeviceProtosTestUtil.java"], tool_files: ["partition_aconfig_flags_paths.txt"], cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)", } java_library { name: "aconfig_device_paths_java_util", srcs: [":java_device_paths_test_util_src"], static_libs: [ "libaconfig_java_proto_nano", ], sdk_version: "core_platform", apex_available: [ "//apex_available:platform", ], } ================================================ FILE: tools/aconfig/aconfig_device_paths/Cargo.toml ================================================ [package] name = "aconfig_device_paths" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.82" ================================================ FILE: tools/aconfig/aconfig_device_paths/mainline_aconfig_flags_paths.txt ================================================ "/apex/com.android.adservices/etc/aconfig_flags.pb", "/apex/com.android.appsearch/etc/aconfig_flags.pb", "/apex/com.android.art/etc/aconfig_flags.pb", "/apex/com.android.bt/etc/aconfig_flags.pb", "/apex/com.android.cellbroadcast/etc/aconfig_flags.pb", "/apex/com.android.configinfrastructure/etc/aconfig_flags.pb", "/apex/com.android.conscrypt/etc/aconfig_flags.pb", "/apex/com.android.devicelock/etc/aconfig_flags.pb", "/apex/com.android.healthfitness/etc/aconfig_flags.pb", "/apex/com.android.ipsec/etc/aconfig_flags.pb", "/apex/com.android.media/etc/aconfig_flags.pb", "/apex/com.android.mediaprovider/etc/aconfig_flags.pb", "/apex/com.android.ondevicepersonalization/etc/aconfig_flags.pb", "/apex/com.android.os.statsd/etc/aconfig_flags.pb", "/apex/com.android.permission/etc/aconfig_flags.pb", "/apex/com.android.profiling/etc/aconfig_flags.pb", "/apex/com.android.tethering/etc/aconfig_flags.pb", "/apex/com.android.uwb/etc/aconfig_flags.pb", "/apex/com.android.virt/etc/aconfig_flags.pb", "/apex/com.android.wifi/etc/aconfig_flags.pb", ================================================ FILE: tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt ================================================ "/system/etc/aconfig_flags.pb", "/system_ext/etc/aconfig_flags.pb", "/product/etc/aconfig_flags.pb", "/vendor/etc/aconfig_flags.pb", ================================================ FILE: tools/aconfig/aconfig_device_paths/src/DeviceProtosTemplate.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig; import android.aconfig.nano.Aconfig.parsed_flag; import android.aconfig.nano.Aconfig.parsed_flags; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @hide */ public class DeviceProtos { public static final String[] PATHS = { TEMPLATE }; private static final String APEX_DIR = "/apex"; private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; /** * Returns a list of all on-device aconfig protos. * * May throw an exception if the protos can't be read at the call site. For * example, some of the protos are in the apex/ partition, which is mounted * somewhat late in the boot process. * * @throws IOException if we can't read one of the protos yet * @return a list of all on-device aconfig protos */ public static List loadAndParseFlagProtos() throws IOException { ArrayList result = new ArrayList(); for (String path : parsedFlagsProtoPaths()) { try (FileInputStream inputStream = new FileInputStream(path)) { parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes()); for (parsed_flag flag : parsedFlags.parsedFlag) { result.add(flag); } } } return result; } /** * Returns the list of all on-device aconfig protos paths. * @hide */ public static List parsedFlagsProtoPaths() { ArrayList paths = new ArrayList(Arrays.asList(PATHS)); File apexDirectory = new File(APEX_DIR); if (!apexDirectory.isDirectory()) { return paths; } File[] subdirs = apexDirectory.listFiles(); if (subdirs == null) { return paths; } for (File prefix : subdirs) { // For each mainline modules, there are two directories, one /, // and one @/. Just read the former. if (prefix.getAbsolutePath().contains("@")) { continue; } File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX); if (!protoPath.exists()) { continue; } paths.add(protoPath.getAbsolutePath()); } return paths; } } ================================================ FILE: tools/aconfig/aconfig_device_paths/src/DeviceProtosTestUtilTemplate.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig; import android.aconfig.nano.Aconfig.parsed_flag; import android.aconfig.nano.Aconfig.parsed_flags; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** @hide */ public class DeviceProtosTestUtil { public static final String[] PATHS = { TEMPLATE }; private static final String APEX_DIR = "/apex/"; private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; private static final String SYSTEM_APEX_DIR = "/system/apex"; /** * Returns a list of all on-device aconfig protos. * *

May throw an exception if the protos can't be read at the call site. For example, some of * the protos are in the apex/ partition, which is mounted somewhat late in the boot process. * * @throws IOException if we can't read one of the protos yet * @return a list of all on-device aconfig protos */ public static List loadAndParseFlagProtos() throws IOException { ArrayList result = new ArrayList(); for (String path : parsedFlagsProtoPaths()) { try (FileInputStream inputStream = new FileInputStream(path)) { parsed_flags parsedFlags = parsed_flags.parseFrom(inputStream.readAllBytes()); for (parsed_flag flag : parsedFlags.parsedFlag) { result.add(flag); } } } return result; } /** * Returns the list of all on-device aconfig protos paths. * * @hide */ public static List parsedFlagsProtoPaths() { ArrayList paths = new ArrayList(Arrays.asList(PATHS)); File apexDirectory = new File(SYSTEM_APEX_DIR); if (!apexDirectory.isDirectory()) { return paths; } File[] subdirs = apexDirectory.listFiles(); if (subdirs == null) { return paths; } for (File prefix : subdirs) { String apexName = prefix.getName().replace("com.google", "com"); apexName = apexName.substring(0, apexName.lastIndexOf('.')); File protoPath = new File(APEX_DIR + apexName + APEX_ACONFIG_PATH_SUFFIX); if (!protoPath.exists()) { continue; } paths.add(protoPath.getAbsolutePath()); } return paths; } } ================================================ FILE: tools/aconfig/aconfig_device_paths/src/HostDeviceProtosTemplate.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * A host lib that can read all aconfig proto file paths on a given device. * This lib is only available on device with root access (userdebug/eng). */ public class HostDeviceProtos { /** * An interface that executes ADB command and return the result. */ public static interface AdbCommandExecutor { /** Executes the ADB command. */ String executeAdbCommand(String command); } static final String[] PATHS = { TEMPLATE }; static final String[] MAINLINE_PATHS = { MAINLINE_T }; private static final String APEX_DIR = "/apex"; private static final String RECURSIVELY_LIST_APEX_DIR_COMMAND = "shell su 0 find /apex | grep aconfig_flags"; private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; /** * Returns the list of all on-device aconfig proto paths from host side. */ public static List parsedFlagsProtoPaths(AdbCommandExecutor adbCommandExecutor) { ArrayList paths = new ArrayList(Arrays.asList(PATHS)); String adbCommandOutput = adbCommandExecutor.executeAdbCommand( RECURSIVELY_LIST_APEX_DIR_COMMAND); if (adbCommandOutput == null || adbCommandOutput.isEmpty()) { paths.addAll(Arrays.asList(MAINLINE_PATHS)); return paths; } Set allFiles = new HashSet<>(Arrays.asList(adbCommandOutput.split("\n"))); Set subdirs = allFiles.stream().map(file -> { String[] filePaths = file.split("/"); // The first element is "", the second element is "apex". return filePaths.length > 2 ? filePaths[2] : ""; }).collect(Collectors.toSet()); for (String prefix : subdirs) { // For each mainline modules, there are two directories, one /, // and one @/. Just read the former. if (prefix.contains("@")) { continue; } String protoPath = APEX_DIR + "/" + prefix + APEX_ACONFIG_PATH_SUFFIX; if (allFiles.contains(protoPath)) { paths.add(protoPath); } } return paths; } } ================================================ FILE: tools/aconfig/aconfig_device_paths/src/lib.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! Library for finding all aconfig on-device protobuf file paths. use anyhow::Result; use std::path::PathBuf; use std::fs; fn read_partition_paths() -> Vec { include_str!("../partition_aconfig_flags_paths.txt") .split(',') .map(|s| s.trim().trim_matches('"')) .filter(|s| !s.is_empty()) .map(|s| PathBuf::from(s.to_string())) .collect() } /// Determines all paths that contain an aconfig protobuf file, /// filtering out nonexistent partition protobuf files. pub fn parsed_flags_proto_paths() -> Result> { let mut result: Vec = read_partition_paths().into_iter().filter(|s| s.exists()).collect(); for dir in fs::read_dir("/apex")? { let dir = dir?; // Only scan the currently active version of each mainline module; skip the @version dirs. if dir.file_name().as_encoded_bytes().iter().any(|&b| b == b'@') { continue; } let mut path = PathBuf::from("/apex"); path.push(dir.path()); path.push("etc"); path.push("aconfig_flags.pb"); if path.exists() { result.push(path); } } Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_read_partition_paths() { assert_eq!(read_partition_paths().len(), 4); assert_eq!( read_partition_paths(), vec![ PathBuf::from("/system/etc/aconfig_flags.pb"), PathBuf::from("/system_ext/etc/aconfig_flags.pb"), PathBuf::from("/product/etc/aconfig_flags.pb"), PathBuf::from("/vendor/etc/aconfig_flags.pb") ] ); } } ================================================ FILE: tools/aconfig/aconfig_device_paths/test/Android.bp ================================================ // Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_team: "trendy_team_android_core_experiments", default_applicable_licenses: ["Android-Apache-2.0"], } android_test { name: "aconfig_device_paths_java_test", srcs: [ "src/**/*.java", ], static_libs: [ "androidx.test.runner", "junit", "aconfig_device_paths_java_util", ], test_suites: [ "general-tests", ], platform_apis: true, certificate: "platform", } ================================================ FILE: tools/aconfig/aconfig_device_paths/test/AndroidManifest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig_device_paths/test/src/DeviceProtosTestUtilTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.test; import static org.junit.Assert.assertTrue; import android.aconfig.DeviceProtosTestUtil; import android.aconfig.nano.Aconfig.parsed_flag; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.List; import java.util.Set; @RunWith(JUnit4.class) public class DeviceProtosTestUtilTest { private static final Set PLATFORM_CONTAINERS = Set.of("system", "vendor", "product"); @Test public void testDeviceProtos_loadAndParseFlagProtos() throws Exception { List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); int platformFlags = 0; int mainlineFlags = 0; for (parsed_flag pf : flags) { if (PLATFORM_CONTAINERS.contains(pf.container)) { platformFlags++; } else { mainlineFlags++; } } assertTrue(platformFlags > 3); assertTrue(mainlineFlags > 3); } } ================================================ FILE: tools/aconfig/aconfig_flags/Android.bp ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ rust_library { name: "libaconfig_flags", crate_name: "aconfig_flags", srcs: [ "src/lib.rs", ], rustlibs: [ "libaconfig_flags_rust", ], host_supported: true, apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } aconfig_declarations { name: "aconfig_flags", package: "com.android.aconfig.flags", container: "system", srcs: ["flags.aconfig"], } rust_aconfig_library { name: "libaconfig_flags_rust", crate_name: "aconfig_flags_rust", aconfig_declarations: "aconfig_flags", host_supported: true, apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } cc_aconfig_library { name: "libaconfig_flags_cc", aconfig_declarations: "aconfig_flags", } java_aconfig_library { name: "aconfig_flags_java", aconfig_declarations: "aconfig_flags", } ================================================ FILE: tools/aconfig/aconfig_flags/Cargo.toml ================================================ [package] name = "aconfig_flags" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] ================================================ FILE: tools/aconfig/aconfig_flags/flags.aconfig ================================================ package: "com.android.aconfig.flags" container: "system" flag { name: "enable_only_new_storage" namespace: "core_experiments_team_internal" bug: "312235596" description: "When enabled, aconfig flags are read from the new aconfig storage only." } flag { name: "enable_aconfigd_from_mainline" namespace: "core_experiments_team_internal" bug: "369808805" description: "When enabled, launch aconfigd from config infra module." } flag { name: "tools_read_from_new_storage" namespace: "core_experiments_team_internal" bug: "370499640" description: "When enabled, tools read directly from the new aconfig storage." } flag { name: "tools_read_from_new_storage_bugfix" namespace: "core_experiments_team_internal" bug: "370499640" description: "When enabled, tools read directly from the new aconfig storage." metadata { purpose: PURPOSE_BUGFIX } } flag { name: "invoke_updatable_aflags" namespace: "core_experiments_team_internal" bug: "385383899" description: "When enabled, the system aflags binary invokes the updatable aflags." metadata { purpose: PURPOSE_BUGFIX } } ================================================ FILE: tools/aconfig/aconfig_flags/src/lib.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig_flags` is a crate for reading aconfig flags from Rust // When building with the Android tool-chain // // - the flag functions will read from aconfig_flags_inner // - the feature "cargo" will be disabled // // When building with cargo // // - the flag functions will all return some trivial value, like true // - the feature "cargo" will be enabled // // This module hides these differences from the rest of aconfig. /// Module used when building with the Android tool-chain #[cfg(not(feature = "cargo"))] pub mod auto_generated { /// Returns the value for the enable_only_new_storage flag. pub fn enable_only_new_storage() -> bool { aconfig_flags_rust::enable_only_new_storage() } /// Returns the value for the enable_aconfigd_from_mainline flag. pub fn enable_aconfigd_from_mainline() -> bool { aconfig_flags_rust::enable_only_new_storage() } /// Returns the value for the invoke_updatable_aflags flag. pub fn invoke_updatable_aflags() -> bool { aconfig_flags_rust::invoke_updatable_aflags() } } /// Module used when building with cargo #[cfg(feature = "cargo")] pub mod auto_generated { /// Returns a placeholder value for the enable_only_new_storage flag. pub fn enable_only_new_storage() -> bool { // Used only to enable typechecking and testing with cargo true } /// Returns a placeholder value for the enable_aconfigd_from_mainline flag. pub fn enable_aconfigd_from_mainline() -> bool { // Used only to enable typechecking and testing with cargo true } /// Returns the value for the invoke_updatable_aflags flag. pub fn invoke_updatable_aflags() -> bool { // Used only to enable typechecking and testing with cargo true } } ================================================ FILE: tools/aconfig/aconfig_protos/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } // proto libraries for consumers of `aconfig dump --format=protobuf` output java_library { name: "libaconfig_java_proto_lite", host_supported: true, srcs: ["protos/aconfig.proto"], static_libs: ["libprotobuf-java-lite"], proto: { type: "lite", }, sdk_version: "current", min_sdk_version: "UpsideDownCake", apex_available: [ "com.android.configinfrastructure", "//apex_available:platform", ], } java_library { name: "libaconfig_java_proto_nano", srcs: ["protos/aconfig.proto"], static_libs: ["libprotobuf-java-nano"], proto: { type: "nano", }, sdk_version: "current", min_sdk_version: "UpsideDownCake", apex_available: [ "//apex_available:platform", ], jarjar_rules: "jarjar-nano-rules.txt", } java_library_host { name: "libaconfig_java_proto_full", srcs: ["protos/aconfig.proto"], static_libs: ["libprotobuf-java-full"], proto: { type: "full", }, } python_library_host { name: "libaconfig_python_proto", srcs: ["protos/aconfig.proto"], proto: { canonical_path_from_root: false, }, } rust_protobuf { name: "libaconfig_rust_proto", protos: ["protos/aconfig.proto"], crate_name: "aconfig_rust_proto", source_stem: "aconfig_rust_proto", host_supported: true, apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } rust_defaults { name: "aconfig_protos.defaults", edition: "2021", clippy_lints: "android", lints: "android", srcs: ["src/lib.rs"], rustlibs: [ "libaconfig_rust_proto", "libanyhow", "libprotobuf", ], proc_macros: [ "libpaste", ], } rust_library { name: "libaconfig_protos", crate_name: "aconfig_protos", host_supported: true, defaults: ["aconfig_protos.defaults"], apex_available: [ "//apex_available:platform", "com.android.configinfrastructure", ], min_sdk_version: "34", } rust_test_host { name: "aconfig_protos.test", test_suites: ["general-tests"], defaults: ["aconfig_protos.defaults"], } // Internal protos python_library_host { name: "aconfig_internal_proto_python", srcs: ["protos/aconfig_internal.proto"], proto: { canonical_path_from_root: false, }, } ================================================ FILE: tools/aconfig/aconfig_protos/Cargo.toml ================================================ [package] name = "aconfig_protos" version = "0.1.0" edition = "2021" build = "build.rs" [features] default = ["cargo"] cargo = [] [dependencies] anyhow = "1.0.69" paste = "1.0.11" protobuf = "3.2.0" [build-dependencies] protobuf-codegen = "3.2.0" ================================================ FILE: tools/aconfig/aconfig_protos/build.rs ================================================ use protobuf_codegen::Codegen; fn main() { let proto_files = vec!["protos/aconfig.proto"]; // tell cargo to only re-run the build script if any of the proto files has changed for path in &proto_files { println!("cargo:rerun-if-changed={}", path); } Codegen::new() .pure() .include("protos") .inputs(proto_files) .cargo_out_dir("aconfig_proto") .run_from_script(); } ================================================ FILE: tools/aconfig/aconfig_protos/jarjar-nano-rules.txt ================================================ rule com.google.protobuf.** android.internal.framework.protobuf.@1 ================================================ FILE: tools/aconfig/aconfig_protos/protos/aconfig.proto ================================================ // Copyright (C) 2023 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License // This is the schema definition for aconfig files. Modifications need to be // either backwards compatible, or include updates to all aconfig files in the // Android tree. syntax = "proto2"; package android.aconfig; // This protobuf file defines messages used to represent and manage flags in the "aconfig" system // The following format requirements apply across various message fields: // // # name: name of the flag // // format: a lowercase string in snake_case format, no consecutive underscores, and no leading // digit. For example adjust_rate is a valid name, while AdjustRate, adjust__rate, and // adjust_rate are invalid // // # namespace: namespace the flag belongs to // // format: a lowercase string in snake_case format, no consecutive underscores, and no leading // digit. For example android_bar_system // // # package: package to which the flag belongs // // format: lowercase strings in snake_case format, delimited by dots, no consecutive underscores // and no leading digit in each string. For example com.android.mypackage is a valid name // while com.android.myPackage, com.android.1mypackage are invalid // // # container: container as software built in its entirety using the same build environment and // always installed as a single unit // // For example the following are all separate containers: // * the system partition // * the vendor partition // * apexes: each APEX is its own container // * APKs: for APKs which are released independently via Play, each APK is its own container. // If an APK is released as part of a Mainline module, or as part of the system partition // via OTA, then they are part of the apex or the system partition container // // format: lowercase strings in snake_case format, delimited by dots if multiple, no consecutive // underscores or leading digits in each string. The recommended container values are the // partition names or the module names // messages used in both aconfig input and output enum flag_state { ENABLED = 1; DISABLED = 2; } enum flag_permission { READ_ONLY = 1; READ_WRITE = 2; } // aconfig input messages: flag declarations and values message flag_declaration { // Name of the flag (required) // See # name for format detail optional string name = 1; // Namespace the flag belongs to (required) // See # namespace for format detail optional string namespace = 2; // Textual description of the flag's purpose (required) optional string description = 3; // Single bug id related to the flag (required) repeated string bug = 4; // Indicates if the flag is permanently read-only and cannot be changed // via release configs (optional) // Default value false optional bool is_fixed_read_only = 5; // Indicates if the flag is exported and accessible beyond its originating container (optional) // Default value false optional bool is_exported = 6; // Additional information about the flag, including its purpose and form factors (optional) optional flag_metadata metadata = 7; }; // Optional metadata about the flag, such as its purpose and its intended form factors. // Can influence the applied policies and testing strategy. message flag_metadata { enum flag_purpose { PURPOSE_UNSPECIFIED = 0; PURPOSE_FEATURE = 1; PURPOSE_BUGFIX = 2; } optional flag_purpose purpose = 1; // TODO(b/315025930): Add field to designate intended target device form factor(s), such as phone, watch or other. } message flag_declarations { // Package to which the flag belongs (required) // See # package for format detail optional string package = 1; // List of flag_declaration objects (required) repeated flag_declaration flag = 2; // Container the flag belongs to (optional) // See # container for format detail optional string container = 3; }; message flag_value { // Package to which the flag belongs (required) // See # package for format detail optional string package = 1; // Name of the flag (required) // See # name for format detail optional string name = 2; optional flag_state state = 3; optional flag_permission permission = 4; }; message flag_values { repeated flag_value flag_value = 1; }; // aconfig output messages: parsed and verified flag declarations and values message tracepoint { // path to declaration or value file relative to $TOP optional string source = 1; optional flag_state state = 2; optional flag_permission permission = 3; } message parsed_flag { // Package to which the flag belongs (required) // See # package for format detail optional string package = 1; // Name of the flag (required) // See # name for format detail optional string name = 2; // Namespace the flag belongs to (required) // See # namespace for format detail optional string namespace = 3; // Textual description of the flag's purpose (required) optional string description = 4; // Single bug id related to the flag (required) repeated string bug = 5; optional flag_state state = 6; optional flag_permission permission = 7; repeated tracepoint trace = 8; // Indicates if the flag is permanently read-only and cannot be changed // via release configs (optional) // Default value false optional bool is_fixed_read_only = 9; // Indicates if the flag is exported and accessible beyond its originating container (optional) // Default value false optional bool is_exported = 10; // Container the flag belongs to (optional) // See # container for format detail optional string container = 11; // Additional information about the flag, including its purpose and form factors (optional) optional flag_metadata metadata = 12; } message parsed_flags { repeated parsed_flag parsed_flag = 1; } ================================================ FILE: tools/aconfig/aconfig_protos/protos/aconfig_internal.proto ================================================ // Copyright (C) 2023 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License // This is the schema definition for protos intended for internal aconfig // use ONLY. There are no guarantees regarding backwards compatibility. // Do not put protos here intended for storage or communication. syntax = "proto2"; package android.aconfig_internal; // This protobuf defines messages used to store data about flags used to guard // APIs which are finalized for a given SDK. message finalized_flag { // Name of the flag (required). Does not include package name. // Must match flag name in the aconfig declaration header. optional string name = 1; // Package the flag belongs to (required). Must match package in the aconfig declaration header. optional string package = 2; // SDK level in which the flag was finalized. optional int32 min_sdk = 3; // TODO - b/378936061: Add support for minor SDK version & SDK extension. }; message finalized_flags { repeated finalized_flag finalized_flag = 1; } ================================================ FILE: tools/aconfig/aconfig_protos/src/lib.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig_protos` is a crate for the protos defined for aconfig // When building with the Android tool-chain // // - an external crate `aconfig_protos` will be generated // - the feature "cargo" will be disabled // // When building with cargo // // - a local sub-module will be generated in OUT_DIR and included in this file // - the feature "cargo" will be enabled // // This module hides these differences from the rest of aconfig. // ---- When building with the Android tool-chain ---- #[cfg(not(feature = "cargo"))] mod auto_generated { pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose; pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration; pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations; pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata; pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission; pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState; pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue; pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues; pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag; pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags; pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint; } // ---- When building with cargo ---- #[cfg(feature = "cargo")] mod auto_generated { // include! statements should be avoided (because they import file contents verbatim), but // because this is only used during local development, and only if using cargo instead of the // Android tool-chain, we allow it include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs")); pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose; pub use aconfig::Flag_declaration as ProtoFlagDeclaration; pub use aconfig::Flag_declarations as ProtoFlagDeclarations; pub use aconfig::Flag_metadata as ProtoFlagMetadata; pub use aconfig::Flag_permission as ProtoFlagPermission; pub use aconfig::Flag_state as ProtoFlagState; pub use aconfig::Flag_value as ProtoFlagValue; pub use aconfig::Flag_values as ProtoFlagValues; pub use aconfig::Parsed_flag as ProtoParsedFlag; pub use aconfig::Parsed_flags as ProtoParsedFlags; pub use aconfig::Tracepoint as ProtoTracepoint; } // ---- Common for both the Android tool-chain and cargo ---- pub use auto_generated::*; use anyhow::Result; use paste::paste; /// Path to proto file const ACONFIG_PROTO_PATH: &str = "//build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto"; /// Check if the name identifier is valid pub fn is_valid_name_ident(s: &str) -> bool { // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed if s.contains("__") { return false; } let mut chars = s.chars(); let Some(first) = chars.next() else { return false; }; if !first.is_ascii_lowercase() { return false; } chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_') } /// Check if the package identifier is valid pub fn is_valid_package_ident(s: &str) -> bool { if !s.contains('.') { return false; } s.split('.').all(is_valid_name_ident) } /// Check if the container identifier is valid pub fn is_valid_container_ident(s: &str) -> bool { s.split('.').all(is_valid_name_ident) } fn try_from_text_proto(s: &str) -> Result where T: protobuf::MessageFull, { protobuf::text_format::parse_from_str(s).map_err(|e| e.into()) } macro_rules! ensure_required_fields { ($type:expr, $struct:expr, $($field:expr),+) => { $( paste! { ensure!($struct.[](), "bad {}: missing {}", $type, $field); } )+ }; } /// Utility module for flag_declaration proto pub mod flag_declaration { use super::*; use anyhow::ensure; /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> { ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description"); ensure!( is_valid_name_ident(pdf.name()), "bad flag declaration: bad name {} expected snake_case string; \ see {ACONFIG_PROTO_PATH} for details", pdf.name() ); ensure!( is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad namespace {} expected snake_case string; \ see {ACONFIG_PROTO_PATH} for details", pdf.namespace() ); ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description"); ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required"); Ok(()) } } /// Utility module for flag_declarations proto pub mod flag_declarations { use super::*; use anyhow::ensure; /// Construct a proto instance from a textproto string content pub fn try_from_text_proto(s: &str) -> Result { let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?; verify_fields(&pdf)?; Ok(pdf) } /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> { ensure_required_fields!("flag declarations", pdf, "package"); // TODO(b/312769710): Make the container field required. ensure!( is_valid_package_ident(pdf.package()), "bad flag declarations: bad package {} expected snake_case strings delimited by dots; \ see {ACONFIG_PROTO_PATH} for details", pdf.package() ); ensure!( !pdf.has_container() || is_valid_container_ident(pdf.container()), "bad flag declarations: bad container" ); for flag_declaration in pdf.flag.iter() { super::flag_declaration::verify_fields(flag_declaration)?; } Ok(()) } } /// Utility module for flag_value proto pub mod flag_value { use super::*; use anyhow::ensure; /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> { ensure_required_fields!("flag value", fv, "package", "name", "state", "permission"); ensure!( is_valid_package_ident(fv.package()), "bad flag value: bad package {} expected snake_case strings delimited by dots; \ see {ACONFIG_PROTO_PATH} for details", fv.package() ); ensure!( is_valid_name_ident(fv.name()), "bad flag value: bad name {} expected snake_case string; \ see {ACONFIG_PROTO_PATH} for details", fv.name() ); Ok(()) } } /// Utility module for flag_values proto pub mod flag_values { use super::*; /// Construct a proto instance from a textproto string content pub fn try_from_text_proto(s: &str) -> Result { let pfv: ProtoFlagValues = super::try_from_text_proto(s)?; verify_fields(&pfv)?; Ok(pfv) } /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> { for flag_value in pfv.flag_value.iter() { super::flag_value::verify_fields(flag_value)?; } Ok(()) } } /// Utility module for flag_permission proto enum pub mod flag_permission { use super::*; use anyhow::bail; /// Construct a flag permission proto enum from string pub fn parse_from_str(permission: &str) -> Result { match permission.to_ascii_lowercase().as_str() { "read_write" => Ok(ProtoFlagPermission::READ_WRITE), "read_only" => Ok(ProtoFlagPermission::READ_ONLY), _ => bail!("Permission needs to be read_only or read_write."), } } /// Serialize flag permission proto enum to string pub fn to_string(permission: &ProtoFlagPermission) -> &str { match permission { ProtoFlagPermission::READ_WRITE => "read_write", ProtoFlagPermission::READ_ONLY => "read_only", } } } /// Utility module for tracepoint proto pub mod tracepoint { use super::*; use anyhow::ensure; /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> { ensure_required_fields!("tracepoint", tp, "source", "state", "permission"); ensure!(!tp.source().is_empty(), "bad tracepoint: empty source"); Ok(()) } } /// Utility module for parsed_flag proto pub mod parsed_flag { use super::*; use anyhow::ensure; /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> { ensure_required_fields!( "parsed flag", pf, "package", "name", "namespace", "description", "state", "permission" ); ensure!( is_valid_package_ident(pf.package()), "bad parsed flag: bad package {} expected snake_case strings delimited by dots; \ see {ACONFIG_PROTO_PATH} for details", pf.package() ); ensure!( !pf.has_container() || is_valid_container_ident(pf.container()), "bad parsed flag: bad container" ); ensure!( is_valid_name_ident(pf.name()), "bad parsed flag: bad name {} expected snake_case string; \ see {ACONFIG_PROTO_PATH} for details", pf.name() ); ensure!( is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace {} expected snake_case string; \ see {ACONFIG_PROTO_PATH} for details", pf.namespace() ); ensure!(!pf.description().is_empty(), "bad parsed flag: empty description"); ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace"); for tp in pf.trace.iter() { super::tracepoint::verify_fields(tp)?; } ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required"); if pf.is_fixed_read_only() { ensure!( pf.permission() == ProtoFlagPermission::READ_ONLY, "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY" ); for tp in pf.trace.iter() { ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY, "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY" ); } } Ok(()) } /// Get the file path of the corresponding flag declaration pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str { debug_assert!(!pf.trace.is_empty()); pf.trace[0].source() } } /// Utility module for parsed_flags proto pub mod parsed_flags { use super::*; use anyhow::bail; use std::cmp::Ordering; /// Construct a proto instance from a binary proto bytes pub fn try_from_binary_proto(bytes: &[u8]) -> Result { let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?; verify_fields(&message)?; Ok(message) } /// Ensure the proto instance is valid by checking its fields pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> { use crate::parsed_flag::path_to_declaration; let mut previous: Option<&ProtoParsedFlag> = None; for parsed_flag in pf.parsed_flag.iter() { if let Some(prev) = previous { let a = create_sorting_key(prev); let b = create_sorting_key(parsed_flag); match a.cmp(&b) { Ordering::Less => {} Ordering::Equal => bail!( "bad parsed flags: duplicate flag {} (defined in {} and {})", a, path_to_declaration(prev), path_to_declaration(parsed_flag) ), Ordering::Greater => { bail!("bad parsed flags: not sorted: {} comes before {}", a, b) } } } super::parsed_flag::verify_fields(parsed_flag)?; previous = Some(parsed_flag); } Ok(()) } /// Merge multipe parsed_flags proto pub fn merge(parsed_flags: Vec, dedup: bool) -> Result { let mut merged = ProtoParsedFlags::new(); for mut pfs in parsed_flags.into_iter() { merged.parsed_flag.append(&mut pfs.parsed_flag); } merged.parsed_flag.sort_by_cached_key(create_sorting_key); if dedup { // Deduplicate identical protobuf messages. Messages with the same sorting key but // different fields (including the path to the original source file) will not be // deduplicated and trigger an error in verify_fields. merged.parsed_flag.dedup(); } verify_fields(&merged)?; Ok(merged) } /// Sort parsed flags pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) { pf.parsed_flag.sort_by_key(create_sorting_key); } fn create_sorting_key(pf: &ProtoParsedFlag) -> String { pf.fully_qualified_name() } } /// ParsedFlagExt trait pub trait ParsedFlagExt { /// Return the fully qualified name fn fully_qualified_name(&self) -> String; } impl ParsedFlagExt for ProtoParsedFlag { fn fully_qualified_name(&self) -> String { format!("{}.{}", self.package(), self.name()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_flag_declarations_try_from_text_proto() { // valid input let flag_declarations = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "system" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" is_exported: true } flag { name: "second" namespace: "second_ns" description: "This is the description of the second flag." bug: "abc" is_fixed_read_only: true } "#, ) .unwrap(); assert_eq!(flag_declarations.package(), "com.foo.bar"); assert_eq!(flag_declarations.container(), "system"); let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap(); assert_eq!(first.name(), "first"); assert_eq!(first.namespace(), "first_ns"); assert_eq!(first.description(), "This is the description of the first flag."); assert_eq!(first.bug, vec!["123"]); assert!(!first.is_fixed_read_only()); assert!(first.is_exported()); let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap(); assert_eq!(second.name(), "second"); assert_eq!(second.namespace(), "second_ns"); assert_eq!(second.description(), "This is the description of the second flag."); assert_eq!(second.bug, vec!["abc"]); assert!(second.is_fixed_read_only()); assert!(!second.is_exported()); // valid input: missing container in flag declarations is supported let flag_declarations = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" } "#, ) .unwrap(); assert_eq!(flag_declarations.container(), ""); assert!(!flag_declarations.has_container()); // bad input: missing package in flag declarations let error = flag_declarations::try_from_text_proto( r#" container: "system" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." } flag { name: "second" namespace: "second_ns" description: "This is the description of the second flag." } "#, ) .unwrap_err(); assert_eq!(format!("{:?}", error), "bad flag declarations: missing package"); // bad input: missing namespace in flag declaration let error = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "system" flag { name: "first" description: "This is the description of the first flag." } flag { name: "second" namespace: "second_ns" description: "This is the description of the second flag." } "#, ) .unwrap_err(); assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace"); // bad input: bad package name in flag declarations let error = flag_declarations::try_from_text_proto( r#" package: "_com.FOO__BAR" container: "system" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." } flag { name: "second" namespace: "second_ns" description: "This is the description of the second flag." } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag declarations: bad package")); // bad input: bad name in flag declaration let error = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "system" flag { name: "FIRST" namespace: "first_ns" description: "This is the description of the first flag." } flag { name: "second" namespace: "second_ns" description: "This is the description of the second flag." } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag declaration: bad name")); // bad input: no bug entries in flag declaration let error = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "system" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required")); // bad input: multiple bug entries in flag declaration let error = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "system" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" bug: "abc" } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required")); // bad input: invalid container name in flag declaration let error = flag_declarations::try_from_text_proto( r#" package: "com.foo.bar" container: "__bad_bad_container.com" flag { name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "123" bug: "abc" } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag declarations: bad container")); // TODO(b/312769710): Verify error when container is missing. } #[test] fn test_flag_values_try_from_text_proto() { // valid input let flag_values = flag_values::try_from_text_proto( r#" flag_value { package: "com.first" name: "first" state: DISABLED permission: READ_ONLY } flag_value { package: "com.second" name: "second" state: ENABLED permission: READ_WRITE } "#, ) .unwrap(); let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap(); assert_eq!(first.package(), "com.first"); assert_eq!(first.name(), "first"); assert_eq!(first.state(), ProtoFlagState::DISABLED); assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY); let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap(); assert_eq!(second.package(), "com.second"); assert_eq!(second.name(), "second"); assert_eq!(second.state(), ProtoFlagState::ENABLED); assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE); // bad input: bad package in flag value let error = flag_values::try_from_text_proto( r#" flag_value { package: "COM.FIRST" name: "first" state: DISABLED permission: READ_ONLY } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag value: bad package")); // bad input: bad name in flag value let error = flag_values::try_from_text_proto( r#" flag_value { package: "com.first" name: "FIRST" state: DISABLED permission: READ_ONLY } "#, ) .unwrap_err(); assert!(format!("{:?}", error).contains("bad flag value: bad name")); // bad input: missing state in flag value let error = flag_values::try_from_text_proto( r#" flag_value { package: "com.first" name: "first" permission: READ_ONLY } "#, ) .unwrap_err(); assert_eq!(format!("{:?}", error), "bad flag value: missing state"); // bad input: missing permission in flag value let error = flag_values::try_from_text_proto( r#" flag_value { package: "com.first" name: "first" state: DISABLED } "#, ) .unwrap_err(); assert_eq!(format!("{:?}", error), "bad flag value: missing permission"); } fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result { use protobuf::Message; let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?; let mut binary_proto = Vec::new(); parsed_flags.write_to_vec(&mut binary_proto)?; parsed_flags::try_from_binary_proto(&binary_proto) } #[test] fn test_parsed_flags_try_from_text_proto() { // valid input let text_proto = r#" parsed_flag { package: "com.first" name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "SOME_BUG" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } parsed_flag { package: "com.second" name: "second" namespace: "second_ns" description: "This is the description of the second flag." bug: "SOME_BUG" state: ENABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } trace { source: "flags.values" state: ENABLED permission: READ_ONLY } is_fixed_read_only: true container: "system" } "#; let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap(); assert_eq!(parsed_flags.parsed_flag.len(), 2); let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap(); assert_eq!(second.package(), "com.second"); assert_eq!(second.name(), "second"); assert_eq!(second.namespace(), "second_ns"); assert_eq!(second.description(), "This is the description of the second flag."); assert_eq!(second.bug, vec!["SOME_BUG"]); assert_eq!(second.state(), ProtoFlagState::ENABLED); assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY); assert_eq!(2, second.trace.len()); assert_eq!(second.trace[0].source(), "flags.declarations"); assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED); assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY); assert_eq!(second.trace[1].source(), "flags.values"); assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED); assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY); assert!(second.is_fixed_read_only()); // valid input: empty let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap(); assert!(parsed_flags.parsed_flag.is_empty()); // bad input: empty trace let text_proto = r#" parsed_flag { package: "com.first" name: "first" namespace: "first_ns" description: "This is the description of the first flag." state: DISABLED permission: READ_ONLY container: "system" } "#; let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace"); // bad input: missing namespace in parsed_flag let text_proto = r#" parsed_flag { package: "com.first" name: "first" description: "This is the description of the first flag." state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace"); // bad input: parsed_flag not sorted by package let text_proto = r#" parsed_flag { package: "bbb.bbb" name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } parsed_flag { package: "aaa.aaa" name: "second" namespace: "second_ns" description: "This is the description of the second flag." bug: "" state: ENABLED permission: READ_WRITE trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err(); assert_eq!( format!("{:?}", error), "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second" ); // bad input: parsed_flag not sorted by name let text_proto = r#" parsed_flag { package: "com.foo" name: "bbb" namespace: "first_ns" description: "This is the description of the first flag." bug: "" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } parsed_flag { package: "com.foo" name: "aaa" namespace: "second_ns" description: "This is the description of the second flag." bug: "" state: ENABLED permission: READ_WRITE trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err(); assert_eq!( format!("{:?}", error), "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa" ); // bad input: duplicate flags let text_proto = r#" parsed_flag { package: "com.foo" name: "bar" namespace: "first_ns" description: "This is the description of the first flag." bug: "" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } parsed_flag { package: "com.foo" name: "bar" namespace: "second_ns" description: "This is the description of the second flag." bug: "" state: ENABLED permission: READ_WRITE trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)"); } #[test] fn test_parsed_flag_path_to_declaration() { let text_proto = r#" parsed_flag { package: "com.foo" name: "bar" namespace: "first_ns" description: "This is the description of the first flag." bug: "b/12345678" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } trace { source: "flags.values" state: ENABLED permission: READ_ONLY } container: "system" } "#; let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap(); let parsed_flag = &parsed_flags.parsed_flag[0]; assert_eq!(crate::parsed_flag::path_to_declaration(parsed_flag), "flags.declarations"); } #[test] fn test_parsed_flags_merge() { let text_proto = r#" parsed_flag { package: "com.first" name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "a" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } parsed_flag { package: "com.second" name: "second" namespace: "second_ns" description: "This is the description of the second flag." bug: "b" state: ENABLED permission: READ_WRITE trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap(); let text_proto = r#" parsed_flag { package: "com.first" name: "first" namespace: "first_ns" description: "This is the description of the first flag." bug: "a" state: DISABLED permission: READ_ONLY trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let first = try_from_binary_proto_from_text_proto(text_proto).unwrap(); let text_proto = r#" parsed_flag { package: "com.second" name: "second" namespace: "second_ns" bug: "b" description: "This is the description of the second flag." state: ENABLED permission: READ_WRITE trace { source: "flags.declarations" state: DISABLED permission: READ_ONLY } container: "system" } "#; let second = try_from_binary_proto_from_text_proto(text_proto).unwrap(); let text_proto = r#" parsed_flag { package: "com.second" name: "second" namespace: "second_ns" bug: "b" description: "This is the description of the second flag." state: ENABLED permission: READ_WRITE trace { source: "duplicate/flags.declarations" state: DISABLED permission: READ_ONLY } } "#; let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap(); // bad cases // two of the same flag with dedup disabled let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)"); // two conflicting flags with dedup disabled let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)"); // two conflicting flags with dedup enabled let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err(); assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)"); // valid cases assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty()); assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty()); assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap()); assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap()); assert_eq!( expected, parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap() ); assert_eq!( expected, parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap() ); assert_eq!( expected, parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap() ); assert_eq!( expected, parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap() ); // two identical flags with dedup enabled assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap()); } #[test] fn test_is_valid_name_ident() { assert!(is_valid_name_ident("foo")); assert!(is_valid_name_ident("foo_bar_123")); assert!(is_valid_name_ident("foo_")); assert!(!is_valid_name_ident("")); assert!(!is_valid_name_ident("123_foo")); assert!(!is_valid_name_ident("foo-bar")); assert!(!is_valid_name_ident("foo-b\u{00e5}r")); assert!(!is_valid_name_ident("foo__bar")); assert!(!is_valid_name_ident("_foo")); } #[test] fn test_is_valid_package_ident() { assert!(is_valid_package_ident("foo.bar")); assert!(is_valid_package_ident("foo.bar_baz")); assert!(is_valid_package_ident("foo.bar.a123")); assert!(!is_valid_package_ident("foo_bar_123")); assert!(!is_valid_package_ident("foo")); assert!(!is_valid_package_ident("foo._bar")); assert!(!is_valid_package_ident("")); assert!(!is_valid_package_ident("123_foo")); assert!(!is_valid_package_ident("foo-bar")); assert!(!is_valid_package_ident("foo-b\u{00e5}r")); assert!(!is_valid_package_ident("foo.bar.123")); assert!(!is_valid_package_ident(".foo.bar")); assert!(!is_valid_package_ident("foo.bar.")); assert!(!is_valid_package_ident(".")); assert!(!is_valid_package_ident("..")); assert!(!is_valid_package_ident("foo..bar")); assert!(!is_valid_package_ident("foo.__bar")); } #[test] fn test_is_valid_container_ident() { assert!(is_valid_container_ident("foo.bar")); assert!(is_valid_container_ident("foo.bar_baz")); assert!(is_valid_container_ident("foo.bar.a123")); assert!(is_valid_container_ident("foo")); assert!(is_valid_container_ident("foo_bar_123")); assert!(!is_valid_container_ident("")); assert!(!is_valid_container_ident("foo._bar")); assert!(!is_valid_container_ident("_foo")); assert!(!is_valid_container_ident("123_foo")); assert!(!is_valid_container_ident("foo-bar")); assert!(!is_valid_container_ident("foo-b\u{00e5}r")); assert!(!is_valid_container_ident("foo.bar.123")); assert!(!is_valid_container_ident(".foo.bar")); assert!(!is_valid_container_ident("foo.bar.")); assert!(!is_valid_container_ident(".")); assert!(!is_valid_container_ident("..")); assert!(!is_valid_container_ident("foo..bar")); assert!(!is_valid_container_ident("foo.__bar")); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "aconfig_storage_file.defaults", edition: "2021", lints: "none", rustlibs: [ "libanyhow", "libthiserror", "libtempfile", "libprotobuf", "libclap", "libcxx", "libaconfig_storage_protos", "libserde", ], } rust_library { name: "libaconfig_storage_file", crate_name: "aconfig_storage_file", host_supported: true, defaults: ["aconfig_storage_file.defaults"], srcs: ["src/lib.rs"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", vendor_available: true, product_available: true, } rust_binary_host { name: "aconfig-storage", defaults: ["aconfig_storage_file.defaults"], srcs: ["src/main.rs"], rustlibs: [ "libaconfig_storage_file", "libserde_json", ], } rust_test_host { name: "aconfig_storage_file.test", test_suites: ["general-tests"], defaults: ["aconfig_storage_file.defaults"], srcs: ["src/lib.rs"], } rust_protobuf { name: "libaconfig_storage_protos", protos: ["protos/aconfig_storage_metadata.proto"], crate_name: "aconfig_storage_protos", source_stem: "aconfig_storage_protos", host_supported: true, apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", vendor_available: true, product_available: true, } cc_library { name: "libaconfig_storage_protos_cc", proto: { export_proto_headers: true, type: "lite", }, srcs: ["protos/aconfig_storage_metadata.proto"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], host_supported: true, min_sdk_version: "29", vendor_available: true, product_available: true, double_loadable: true, } // cxx source codegen from rust api genrule { name: "libcxx_aconfig_storage_file_bridge_code", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.cc"], } // cxx header codegen from rust api genrule { name: "libcxx_aconfig_storage_file_bridge_header", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) --header > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.h"], } // a static cc lib based on generated code rust_ffi_static { name: "libaconfig_storage_file_cxx_bridge", crate_name: "aconfig_storage_file_cxx_bridge", host_supported: true, vendor_available: true, product_available: true, srcs: ["src/lib.rs"], defaults: ["aconfig_storage_file.defaults"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", } // storage file parse api cc interface cc_library { name: "libaconfig_storage_file_cc", srcs: ["aconfig_storage_file.cpp"], generated_headers: [ "cxx-bridge-header", "libcxx_aconfig_storage_file_bridge_header", ], generated_sources: ["libcxx_aconfig_storage_file_bridge_code"], whole_static_libs: ["libaconfig_storage_file_cxx_bridge"], export_include_dirs: ["include"], host_supported: true, vendor_available: true, product_available: true, shared_libs: [ "libbase", ], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", double_loadable: true, } // storage file parse api java library java_library { name: "aconfig_storage_file_java", srcs: [ "srcs/**/*.java", ], sdk_version: "core_current", min_sdk_version: "29", host_supported: true, apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], } // storage file parse api java library for core library java_library { name: "aconfig_storage_file_java_none", srcs: [ "srcs/**/*.java", ], sdk_version: "none", system_modules: "core-all-system-modules", host_supported: true, } ================================================ FILE: tools/aconfig/aconfig_storage_file/Cargo.toml ================================================ [package] name = "aconfig_storage_file" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] anyhow = "1.0.69" protobuf = "3.2.0" tempfile = "3.9.0" thiserror = "1.0.56" clap = { version = "4.1.8", features = ["derive"] } cxx = "1.0" serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" [[bin]] name = "aconfig-storage" path = "src/main.rs" [build-dependencies] protobuf-codegen = "3.2.0" cxx-build = "1.0" ================================================ FILE: tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp ================================================ #include "rust/cxx.h" #include "aconfig_storage/lib.rs.h" #include "aconfig_storage/aconfig_storage_file.hpp" using namespace android::base; namespace aconfig_storage { Result> list_flags( const std::string& package_map, const std::string& flag_map, const std::string& flag_val) { auto flag_list_cxx = list_flags_cxx(rust::Str(package_map.c_str()), rust::Str(flag_map.c_str()), rust::Str(flag_val.c_str())); if (flag_list_cxx.query_success) { auto flag_list = std::vector(); for (const auto& flag_cxx : flag_list_cxx.flags) { auto flag = FlagValueSummary(); flag.package_name = std::string(flag_cxx.package_name); flag.flag_name = std::string(flag_cxx.flag_name); flag.flag_value = std::string(flag_cxx.flag_value); flag.value_type = std::string(flag_cxx.value_type); flag_list.push_back(flag); } return flag_list; } else { return Error() << flag_list_cxx.error_message; } } Result> list_flags_with_info( const std::string& package_map, const std::string& flag_map, const std::string& flag_val, const std::string& flag_info) { auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()), rust::Str(flag_map.c_str()), rust::Str(flag_val.c_str()), rust::Str(flag_info.c_str())); if (flag_list_cxx.query_success) { auto flag_list = std::vector(); for (const auto& flag_cxx : flag_list_cxx.flags) { auto flag = FlagValueAndInfoSummary(); flag.package_name = std::string(flag_cxx.package_name); flag.flag_name = std::string(flag_cxx.flag_name); flag.flag_value = std::string(flag_cxx.flag_value); flag.value_type = std::string(flag_cxx.value_type); flag.is_readwrite = flag_cxx.is_readwrite; flag.has_server_override = flag_cxx.has_server_override; flag.has_local_override = flag_cxx.has_local_override; flag_list.push_back(flag); } return flag_list; } else { return Error() << flag_list_cxx.error_message; } } } // namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_file/build.rs ================================================ use protobuf_codegen::Codegen; fn main() { let proto_files = vec!["protos/aconfig_storage_metadata.proto"]; // tell cargo to only re-run the build script if any of the proto files has changed for path in &proto_files { println!("cargo:rerun-if-changed={}", path); } Codegen::new() .pure() .include("protos") .inputs(proto_files) .cargo_out_dir("aconfig_storage_protos") .run_from_script(); let _ = cxx_build::bridge("src/lib.rs"); } ================================================ FILE: tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp ================================================ #pragma once #include #include #include namespace aconfig_storage { /// Flag value summary for a flag struct FlagValueSummary { std::string package_name; std::string flag_name; std::string flag_value; std::string value_type; }; /// List all flag values /// \input package_map: package map file /// \input flag_map: flag map file /// \input flag_val: flag value file android::base::Result> list_flags( const std::string& package_map, const std::string& flag_map, const std::string& flag_val); /// Flag value and info summary for a flag struct FlagValueAndInfoSummary { std::string package_name; std::string flag_name; std::string flag_value; std::string value_type; bool is_readwrite; bool has_server_override; bool has_local_override; }; /// List all flag values with their flag info /// \input package_map: package map file /// \input flag_map: flag map file /// \input flag_val: flag value file /// \input flag_info: flag info file android::base::Result> list_flags_with_info( const std::string& package_map, const std::string& flag_map, const std::string& flag_val, const std::string& flag_info); }// namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto ================================================ // Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License // This is the schema definition for aconfig files. Modifications need to be // either backwards compatible, or include updates to all aconfig files in the // Android tree. syntax = "proto2"; package android.aconfig_storage_metadata; message storage_file_info { optional uint32 version = 1; optional string container = 2; optional string package_map = 3; optional string flag_map = 4; optional string flag_val = 5; optional string flag_info = 6; optional int64 timestamp = 7; } message storage_files { repeated storage_file_info files = 1; }; ================================================ FILE: tools/aconfig/aconfig_storage_file/src/flag_info.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag info module defines the flag info file format and methods for serialization //! and deserialization use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use std::fmt; /// Flag info header struct #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagInfoHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_flags: u32, pub boolean_flag_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagInfoHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Flags: {}, Boolean Flag Offset:{}", self.num_flags, self.boolean_flag_offset )?; Ok(()) } } impl FlagInfoHeader { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.version.to_le_bytes()); let container_bytes = self.container.as_bytes(); result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(container_bytes); result.extend_from_slice(&self.file_type.to_le_bytes()); result.extend_from_slice(&self.file_size.to_le_bytes()); result.extend_from_slice(&self.num_flags.to_le_bytes()); result.extend_from_slice(&self.boolean_flag_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let list = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_flags: read_u32_from_bytes(bytes, &mut head)?, boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?, }; if list.file_type != StorageFileType::FlagInfo as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a flag info file" ))); } Ok(list) } } /// bit field for flag info #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum FlagInfoBit { HasServerOverride = 1 << 0, IsReadWrite = 1 << 1, HasLocalOverride = 1 << 2, } /// Flag info node struct #[derive(PartialEq, Clone, Serialize, Deserialize)] pub struct FlagInfoNode { pub attributes: u8, } /// Implement debug print trait for node impl fmt::Debug for FlagInfoNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "readwrite: {}, server override: {}, local override: {}", self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0, self.attributes & (FlagInfoBit::HasServerOverride as u8) != 0, self.attributes & (FlagInfoBit::HasLocalOverride as u8) != 0, )?; Ok(()) } } impl FlagInfoNode { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.attributes.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? }; Ok(node) } /// Create flag info node pub fn create(is_flag_rw: bool) -> Self { Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } } } } /// Flag info list struct #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagInfoList { pub header: FlagInfoHeader, pub nodes: Vec, } /// Implement debug print trait for flag info list impl fmt::Debug for FlagInfoList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } impl FlagInfoList { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { [ self.header.into_bytes(), self.nodes.iter().map(|v| v.into_bytes()).collect::>().concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let header = FlagInfoHeader::from_bytes(bytes)?; let num_flags = header.num_flags; let mut head = header.into_bytes().len(); let nodes = (0..num_flags) .map(|_| { let node = FlagInfoNode::from_bytes(&bytes[head..])?; head += node.into_bytes().len(); Ok(node) }) .collect::, AconfigStorageError>>() .map_err(|errmsg| { AconfigStorageError::BytesParseFail(anyhow!( "fail to parse flag info list: {}", errmsg )) })?; let list = Self { header, nodes }; Ok(list) } } #[cfg(test)] mod tests { use super::*; use crate::{ test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, }; // this test point locks down the value list serialization #[test] fn test_serialization() { for file_version in 1..=MAX_SUPPORTED_FILE_VERSION { let flag_info_list = create_test_flag_info_list(file_version); let header: &FlagInfoHeader = &flag_info_list.header; let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let nodes: &Vec = &flag_info_list.nodes; for node in nodes.iter() { let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap(); assert_eq!(node, &reinterpreted_node); } let flag_info_bytes = flag_info_list.into_bytes(); let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes); assert!(reinterpreted_info_list.is_ok()); assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap()); assert_eq!(flag_info_bytes.len() as u32, header.file_size); } } // this test point locks down that version number should be at the top of serialized // bytes #[test] fn test_version_number() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let bytes = &flag_info_list.into_bytes(); let mut head = 0; let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } // this test point locks down file type check #[test] fn test_file_type_check() { let mut flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); flag_info_list.header.file_type = 123u8; let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a flag info file)") ); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/flag_table.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag table module defines the flag table file format and methods for serialization //! and deserialization use crate::{ get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes, read_u8_from_bytes, }; use crate::{AconfigStorageError, StorageFileType, StoredFlagType}; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use std::fmt; /// Flag table header struct #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagTableHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_flags: u32, pub bucket_offset: u32, pub node_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagTableHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}", self.num_flags, self.bucket_offset, self.node_offset )?; Ok(()) } } impl FlagTableHeader { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.version.to_le_bytes()); let container_bytes = self.container.as_bytes(); result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(container_bytes); result.extend_from_slice(&self.file_type.to_le_bytes()); result.extend_from_slice(&self.file_size.to_le_bytes()); result.extend_from_slice(&self.num_flags.to_le_bytes()); result.extend_from_slice(&self.bucket_offset.to_le_bytes()); result.extend_from_slice(&self.node_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let table = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_flags: read_u32_from_bytes(bytes, &mut head)?, bucket_offset: read_u32_from_bytes(bytes, &mut head)?, node_offset: read_u32_from_bytes(bytes, &mut head)?, }; if table.file_type != StorageFileType::FlagMap as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a flag map" ))); } Ok(table) } } /// Flag table node struct #[derive(PartialEq, Clone, Serialize, Deserialize)] pub struct FlagTableNode { pub package_id: u32, pub flag_name: String, pub flag_type: StoredFlagType, // within package flag index of this flag type pub flag_index: u16, pub next_offset: Option, } /// Implement debug print trait for node impl fmt::Debug for FlagTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Package Id: {}, Flag: {}, Type: {:?}, Index: {}, Next: {:?}", self.package_id, self.flag_name, self.flag_type, self.flag_index, self.next_offset )?; Ok(()) } } impl FlagTableNode { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.package_id.to_le_bytes()); let name_bytes = self.flag_name.as_bytes(); result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(name_bytes); result.extend_from_slice(&(self.flag_type as u16).to_le_bytes()); result.extend_from_slice(&self.flag_index.to_le_bytes()); result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let node = Self { package_id: read_u32_from_bytes(bytes, &mut head)?, flag_name: read_str_from_bytes(bytes, &mut head)?, flag_type: StoredFlagType::try_from(read_u16_from_bytes(bytes, &mut head)?)?, flag_index: read_u16_from_bytes(bytes, &mut head)?, next_offset: match read_u32_from_bytes(bytes, &mut head)? { 0 => None, val => Some(val), }, }; Ok(node) } /// Calculate node bucket index pub fn find_bucket_index(package_id: u32, flag_name: &str, num_buckets: u32) -> u32 { let full_flag_name = package_id.to_string() + "/" + flag_name; get_bucket_index(full_flag_name.as_bytes(), num_buckets) } } #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagTable { pub header: FlagTableHeader, pub buckets: Vec>, pub nodes: Vec, } /// Implement debug print trait for flag table impl fmt::Debug for FlagTable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Buckets:")?; writeln!(f, "{:?}", self.buckets)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } /// Flag table struct impl FlagTable { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { [ self.header.into_bytes(), self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::>().concat(), self.nodes.iter().map(|v| v.into_bytes()).collect::>().concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let header = FlagTableHeader::from_bytes(bytes)?; let num_flags = header.num_flags; let num_buckets = crate::get_table_size(num_flags)?; let mut head = header.into_bytes().len(); let buckets = (0..num_buckets) .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() { 0 => None, val => Some(val), }) .collect(); let nodes = (0..num_flags) .map(|_| { let node = FlagTableNode::from_bytes(&bytes[head..])?; head += node.into_bytes().len(); Ok(node) }) .collect::, AconfigStorageError>>() .map_err(|errmsg| { AconfigStorageError::BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg)) })?; let table = Self { header, buckets, nodes }; Ok(table) } } #[cfg(test)] mod tests { use super::*; use crate::{ test_utils::create_test_flag_table, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, }; // this test point locks down the table serialization #[test] fn test_serialization() { for file_version in 1..=MAX_SUPPORTED_FILE_VERSION { let flag_table = create_test_flag_table(file_version); let header: &FlagTableHeader = &flag_table.header; let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let nodes: &Vec = &flag_table.nodes; for node in nodes.iter() { let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap(); assert_eq!(node, &reinterpreted_node); } let flag_table_bytes = flag_table.into_bytes(); let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes); assert!(reinterpreted_table.is_ok()); assert_eq!(&flag_table, &reinterpreted_table.unwrap()); assert_eq!(flag_table_bytes.len() as u32, header.file_size); } } // this test point locks down that version number should be at the top of serialized // bytes #[test] fn test_version_number() { let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION); let bytes = &flag_table.into_bytes(); let mut head = 0; let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } // this test point locks down file type check #[test] fn test_file_type_check() { let mut flag_table = create_test_flag_table(DEFAULT_FILE_VERSION); flag_table.header.file_type = 123u8; let error = FlagTable::from_bytes(&flag_table.into_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a flag map)") ); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/flag_value.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag value module defines the flag value file format and methods for serialization //! and deserialization use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use std::fmt; /// Flag value header struct #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagValueHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_flags: u32, pub boolean_value_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagValueHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Flags: {}, Value Offset:{}", self.num_flags, self.boolean_value_offset )?; Ok(()) } } impl FlagValueHeader { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.version.to_le_bytes()); let container_bytes = self.container.as_bytes(); result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(container_bytes); result.extend_from_slice(&self.file_type.to_le_bytes()); result.extend_from_slice(&self.file_size.to_le_bytes()); result.extend_from_slice(&self.num_flags.to_le_bytes()); result.extend_from_slice(&self.boolean_value_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let list = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_flags: read_u32_from_bytes(bytes, &mut head)?, boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?, }; if list.file_type != StorageFileType::FlagVal as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a flag value file" ))); } Ok(list) } } /// Flag value list struct #[derive(PartialEq, Serialize, Deserialize)] pub struct FlagValueList { pub header: FlagValueHeader, pub booleans: Vec, } /// Implement debug print trait for flag value impl fmt::Debug for FlagValueList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Values:")?; writeln!(f, "{:?}", self.booleans)?; Ok(()) } } impl FlagValueList { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { [ self.header.into_bytes(), self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::>().concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let header = FlagValueHeader::from_bytes(bytes)?; let num_flags = header.num_flags; let mut head = header.into_bytes().len(); let booleans = (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect(); let list = Self { header, booleans }; Ok(list) } } #[cfg(test)] mod tests { use super::*; use crate::{ test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, }; #[test] // this test point locks down the value list serialization fn test_serialization() { for file_version in 1..=MAX_SUPPORTED_FILE_VERSION { let flag_value_list = create_test_flag_value_list(file_version); let header: &FlagValueHeader = &flag_value_list.header; let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let flag_value_bytes = flag_value_list.into_bytes(); let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes); assert!(reinterpreted_value_list.is_ok()); assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap()); assert_eq!(flag_value_bytes.len() as u32, header.file_size); } } #[test] // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); let bytes = &flag_value_list.into_bytes(); let mut head = 0; let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } #[test] // this test point locks down file type check fn test_file_type_check() { let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); flag_value_list.header.file_type = 123u8; let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a flag value file)") ); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/lib.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig_storage_file` is a crate that defines aconfig storage file format, it //! also includes apis to read flags from storage files. It provides three apis to //! interface with storage files: //! //! 1, function to get package flag value start offset //! pub fn get_package_offset(container: &str, package: &str) -> `Result>>` //! //! 2, function to get flag offset within a specific package //! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result>>` //! //! 3, function to get the actual flag value given the global offset (combined package and //! flag offset). //! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result` //! //! Note these are low level apis that are expected to be only used in auto generated flag //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis //! please refer to the g3doc go/android-flags pub mod flag_info; pub mod flag_table; pub mod flag_value; pub mod package_table; pub mod protos; pub mod sip_hasher13; pub mod test_utils; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fs::File; use std::hash::Hasher; use std::io::Read; pub use crate::flag_info::{FlagInfoBit, FlagInfoHeader, FlagInfoList, FlagInfoNode}; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; pub use crate::sip_hasher13::SipHasher13; use crate::AconfigStorageError::{ BytesParseFail, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType, }; /// The max storage file version from which we can safely read/write. May be /// experimental. pub const MAX_SUPPORTED_FILE_VERSION: u32 = 2; /// The newest fully-released version. Unless otherwise specified, this is the /// version we will write. pub const DEFAULT_FILE_VERSION: u32 = 1; /// Good hash table prime number pub(crate) const HASH_PRIMES: [u32; 29] = [ 7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, ]; /// Storage file type enum #[derive(Clone, Debug, PartialEq, Eq)] pub enum StorageFileType { PackageMap = 0, FlagMap = 1, FlagVal = 2, FlagInfo = 3, } impl TryFrom<&str> for StorageFileType { type Error = anyhow::Error; fn try_from(value: &str) -> std::result::Result { match value { "package_map" => Ok(Self::PackageMap), "flag_map" => Ok(Self::FlagMap), "flag_val" => Ok(Self::FlagVal), "flag_info" => Ok(Self::FlagInfo), _ => Err(anyhow!( "Invalid storage file type, valid types are package_map|flag_map|flag_val|flag_info" )), } } } impl TryFrom for StorageFileType { type Error = anyhow::Error; fn try_from(value: u8) -> Result { match value { x if x == Self::PackageMap as u8 => Ok(Self::PackageMap), x if x == Self::FlagMap as u8 => Ok(Self::FlagMap), x if x == Self::FlagVal as u8 => Ok(Self::FlagVal), x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo), _ => Err(anyhow!("Invalid storage file type")), } } } /// Flag type enum as stored by storage file /// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum StoredFlagType { ReadWriteBoolean = 0, ReadOnlyBoolean = 1, FixedReadOnlyBoolean = 2, } impl TryFrom for StoredFlagType { type Error = AconfigStorageError; fn try_from(value: u16) -> Result { match value { x if x == Self::ReadWriteBoolean as u16 => Ok(Self::ReadWriteBoolean), x if x == Self::ReadOnlyBoolean as u16 => Ok(Self::ReadOnlyBoolean), x if x == Self::FixedReadOnlyBoolean as u16 => Ok(Self::FixedReadOnlyBoolean), _ => Err(InvalidStoredFlagType(anyhow!("Invalid stored flag type"))), } } } /// Flag value type enum, one FlagValueType maps to many StoredFlagType /// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FlagValueType { Boolean = 0, } impl TryFrom for FlagValueType { type Error = AconfigStorageError; fn try_from(value: StoredFlagType) -> Result { match value { StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean), StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean), StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean), } } } impl TryFrom for FlagValueType { type Error = AconfigStorageError; fn try_from(value: u16) -> Result { match value { x if x == Self::Boolean as u16 => Ok(Self::Boolean), _ => Err(InvalidFlagValueType(anyhow!("Invalid flag value type"))), } } } /// Storage query api error #[non_exhaustive] #[derive(thiserror::Error, Debug)] pub enum AconfigStorageError { #[error("failed to read the file")] FileReadFail(#[source] anyhow::Error), #[error("fail to parse protobuf")] ProtobufParseFail(#[source] anyhow::Error), #[error("storage files not found for this container")] StorageFileNotFound(#[source] anyhow::Error), #[error("fail to map storage file")] MapFileFail(#[source] anyhow::Error), #[error("fail to get mapped file")] ObtainMappedFileFail(#[source] anyhow::Error), #[error("fail to flush mapped storage file")] MapFlushFail(#[source] anyhow::Error), #[error("number of items in hash table exceed limit")] HashTableSizeLimit(#[source] anyhow::Error), #[error("failed to parse bytes into data")] BytesParseFail(#[source] anyhow::Error), #[error("cannot parse storage files with a higher version")] HigherStorageFileVersion(#[source] anyhow::Error), #[error("invalid storage file byte offset")] InvalidStorageFileOffset(#[source] anyhow::Error), #[error("failed to create file")] FileCreationFail(#[source] anyhow::Error), #[error("invalid stored flag type")] InvalidStoredFlagType(#[source] anyhow::Error), #[error("invalid flag value type")] InvalidFlagValueType(#[source] anyhow::Error), } /// Get the right hash table size given number of entries in the table. Use a /// load factor of 0.5 for performance. pub fn get_table_size(entries: u32) -> Result { HASH_PRIMES .iter() .find(|&&num| num >= 2 * entries) .copied() .ok_or(HashTableSizeLimit(anyhow!("Number of items in a hash table exceeds limit"))) } /// Get the corresponding bucket index given the key and number of buckets pub(crate) fn get_bucket_index(val: &[u8], num_buckets: u32) -> u32 { let mut s = SipHasher13::new(); s.write(val); s.write_u8(0xff); let ret = (s.finish() % num_buckets as u64) as u32; ret } /// Read and parse bytes as u8 pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result { let val = u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| { BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg)) })?); *head += 1; Ok(val) } /// Read and parse bytes as u16 pub(crate) fn read_u16_from_bytes( buf: &[u8], head: &mut usize, ) -> Result { let val = u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| { BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg)) })?); *head += 2; Ok(val) } /// Read and parse the first 4 bytes of buf as u32. pub fn read_u32_from_start_of_bytes(buf: &[u8]) -> Result { read_u32_from_bytes(buf, &mut 0) } /// Read and parse bytes as u32 pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result { let val = u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| { BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg)) })?); *head += 4; Ok(val) } // Read and parse bytes as u64 pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result { let val = u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| { BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg)) })?); *head += 8; Ok(val) } /// Read and parse bytes as string pub(crate) fn read_str_from_bytes( buf: &[u8], head: &mut usize, ) -> Result { let num_bytes = read_u32_from_bytes(buf, head)? as usize; let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec()) .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?; *head += num_bytes; Ok(val) } /// Read in storage file as bytes pub fn read_file_to_bytes(file_path: &str) -> Result, AconfigStorageError> { let mut file = File::open(file_path).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)) })?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!( "Failed to read bytes from file {}: {}", file_path, errmsg )) })?; Ok(buffer) } /// Flag value summary #[derive(Debug, PartialEq)] pub struct FlagValueSummary { pub package_name: String, pub flag_name: String, pub flag_value: String, pub value_type: StoredFlagType, } /// List flag values from storage files pub fn list_flags( package_map: &str, flag_map: &str, flag_val: &str, ) -> Result, AconfigStorageError> { let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?; let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?; let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?; let mut package_info = vec![("", 0); package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index); } let mut flags = Vec::new(); for node in flag_table.nodes.iter() { let (package_name, boolean_start_index) = package_info[node.package_id as usize]; let flag_index = boolean_start_index + node.flag_index as u32; let flag_value = flag_value_list.booleans[flag_index as usize]; flags.push(FlagValueSummary { package_name: String::from(package_name), flag_name: node.flag_name.clone(), flag_value: flag_value.to_string(), value_type: node.flag_type, }); } flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) { Ordering::Equal => v1.flag_name.cmp(&v2.flag_name), other => other, }); Ok(flags) } /// Flag value and info summary #[derive(Debug, PartialEq)] pub struct FlagValueAndInfoSummary { pub package_name: String, pub flag_name: String, pub flag_value: String, pub value_type: StoredFlagType, pub is_readwrite: bool, pub has_server_override: bool, pub has_local_override: bool, } /// List flag values and info from storage files pub fn list_flags_with_info( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> Result, AconfigStorageError> { let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?; let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?; let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?; let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?; let mut package_info = vec![("", 0); package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index); } let mut flags = Vec::new(); for node in flag_table.nodes.iter() { let (package_name, boolean_start_index) = package_info[node.package_id as usize]; let flag_index = boolean_start_index + node.flag_index as u32; let flag_value = flag_value_list.booleans[flag_index as usize]; let flag_attribute = flag_info.nodes[flag_index as usize].attributes; flags.push(FlagValueAndInfoSummary { package_name: String::from(package_name), flag_name: node.flag_name.clone(), flag_value: flag_value.to_string(), value_type: node.flag_type, is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0, has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0, has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0, }); } flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) { Ordering::Equal => v1.flag_name.cmp(&v2.flag_name), other => other, }); Ok(flags) } // *************************************** // // CC INTERLOP // *************************************** // // Exported rust data structure and methods, c++ code will be generated #[cxx::bridge] mod ffi { /// flag value summary cxx return pub struct FlagValueSummaryCXX { pub package_name: String, pub flag_name: String, pub flag_value: String, pub value_type: String, } /// flag value and info summary cxx return pub struct FlagValueAndInfoSummaryCXX { pub package_name: String, pub flag_name: String, pub flag_value: String, pub value_type: String, pub is_readwrite: bool, pub has_server_override: bool, pub has_local_override: bool, } /// list flag result cxx return pub struct ListFlagValueResultCXX { pub query_success: bool, pub error_message: String, pub flags: Vec, } /// list flag with info result cxx return pub struct ListFlagValueAndInfoResultCXX { pub query_success: bool, pub error_message: String, pub flags: Vec, } // Rust export to c++ extern "Rust" { pub fn list_flags_cxx( package_map: &str, flag_map: &str, flag_val: &str, ) -> ListFlagValueResultCXX; pub fn list_flags_with_info_cxx( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> ListFlagValueAndInfoResultCXX; } } /// implement flag value summary cxx return type impl ffi::FlagValueSummaryCXX { pub(crate) fn new(summary: FlagValueSummary) -> Self { Self { package_name: summary.package_name, flag_name: summary.flag_name, flag_value: summary.flag_value, value_type: format!("{:?}", summary.value_type), } } } /// implement flag value and info summary cxx return type impl ffi::FlagValueAndInfoSummaryCXX { pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self { Self { package_name: summary.package_name, flag_name: summary.flag_name, flag_value: summary.flag_value, value_type: format!("{:?}", summary.value_type), is_readwrite: summary.is_readwrite, has_server_override: summary.has_server_override, has_local_override: summary.has_local_override, } } } /// implement list flag cxx interlop pub fn list_flags_cxx( package_map: &str, flag_map: &str, flag_val: &str, ) -> ffi::ListFlagValueResultCXX { match list_flags(package_map, flag_map, flag_val) { Ok(summary) => ffi::ListFlagValueResultCXX { query_success: true, error_message: String::new(), flags: summary.into_iter().map(ffi::FlagValueSummaryCXX::new).collect(), }, Err(errmsg) => ffi::ListFlagValueResultCXX { query_success: false, error_message: format!("{:?}", errmsg), flags: Vec::new(), }, } } /// implement list flag with info cxx interlop pub fn list_flags_with_info_cxx( package_map: &str, flag_map: &str, flag_val: &str, flag_info: &str, ) -> ffi::ListFlagValueAndInfoResultCXX { match list_flags_with_info(package_map, flag_map, flag_val, flag_info) { Ok(summary) => ffi::ListFlagValueAndInfoResultCXX { query_success: true, error_message: String::new(), flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(), }, Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX { query_success: false, error_message: format!("{:?}", errmsg), flags: Vec::new(), }, } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::{ create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list, create_test_package_table, write_bytes_to_temp_file, }; #[test] // this test point locks down the flag list api fn test_list_flag() { let package_table = write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes()) .unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes()) .unwrap(); let flag_value_list = write_bytes_to_temp_file( &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(), ) .unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_value_list_path = flag_value_list.path().display().to_string(); let flags = list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap(); let expected = [ FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("disabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("false"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("enabled_ro"), value_type: StoredFlagType::ReadOnlyBoolean, flag_value: String::from("true"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("enabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("true"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("disabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("false"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("enabled_fixed_ro"), value_type: StoredFlagType::FixedReadOnlyBoolean, flag_value: String::from("true"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("enabled_ro"), value_type: StoredFlagType::ReadOnlyBoolean, flag_value: String::from("true"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_4"), flag_name: String::from("enabled_fixed_ro"), value_type: StoredFlagType::FixedReadOnlyBoolean, flag_value: String::from("true"), }, FlagValueSummary { package_name: String::from("com.android.aconfig.storage.test_4"), flag_name: String::from("enabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("true"), }, ]; assert_eq!(flags, expected); } #[test] // this test point locks down the flag list with info api fn test_list_flag_with_info() { let package_table = write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes()) .unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes()) .unwrap(); let flag_value_list = write_bytes_to_temp_file( &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(), ) .unwrap(); let flag_info_list = write_bytes_to_temp_file( &create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(), ) .unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_value_list_path = flag_value_list.path().display().to_string(); let flag_info_list_path = flag_info_list.path().display().to_string(); let flags = list_flags_with_info( &package_table_path, &flag_table_path, &flag_value_list_path, &flag_info_list_path, ) .unwrap(); let expected = [ FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("disabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("false"), is_readwrite: true, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("enabled_ro"), value_type: StoredFlagType::ReadOnlyBoolean, flag_value: String::from("true"), is_readwrite: false, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_1"), flag_name: String::from("enabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("true"), is_readwrite: true, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("disabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("false"), is_readwrite: true, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("enabled_fixed_ro"), value_type: StoredFlagType::FixedReadOnlyBoolean, flag_value: String::from("true"), is_readwrite: false, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_2"), flag_name: String::from("enabled_ro"), value_type: StoredFlagType::ReadOnlyBoolean, flag_value: String::from("true"), is_readwrite: false, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_4"), flag_name: String::from("enabled_fixed_ro"), value_type: StoredFlagType::FixedReadOnlyBoolean, flag_value: String::from("true"), is_readwrite: false, has_server_override: false, has_local_override: false, }, FlagValueAndInfoSummary { package_name: String::from("com.android.aconfig.storage.test_4"), flag_name: String::from("enabled_rw"), value_type: StoredFlagType::ReadWriteBoolean, flag_value: String::from("true"), is_readwrite: true, has_server_override: false, has_local_override: false, }, ]; assert_eq!(flags, expected); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/main.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig-storage` is a debugging tool to parse storage files use aconfig_storage_file::{ list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList, PackageTable, StorageFileType, }; use clap::{builder::ArgAction, Arg, Command}; use serde::Serialize; use serde_json; use std::fmt; use std::fs; use std::fs::File; use std::io::Write; /** * Usage Examples * * Print file: * $ aconfig-storage print --file=path/to/flag.map --type=flag_map * * List flags: * $ aconfig-storage list --flag-map=path/to/flag.map \ * --flag-val=path/to/flag.val --package-map=path/to/package.map * * Write binary file for testing: * $ aconfig-storage print --file=path/to/flag.map --type=flag_map --format=json > flag_map.json * $ vim flag_map.json // Manually make updates * $ aconfig-storage write-bytes --input-file=flag_map.json --output-file=path/to/flag.map --type=flag_map */ fn cli() -> Command { Command::new("aconfig-storage") .subcommand_required(true) .subcommand( Command::new("print") .arg(Arg::new("file").long("file").required(true).action(ArgAction::Set)) .arg( Arg::new("type") .long("type") .required(true) .value_parser(|s: &str| StorageFileType::try_from(s)), ) .arg(Arg::new("format").long("format").required(false).action(ArgAction::Set)), ) .subcommand( Command::new("list") .arg( Arg::new("package-map") .long("package-map") .required(true) .action(ArgAction::Set), ) .arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set)) .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)) .arg( Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set), ), ) .subcommand( Command::new("write-bytes") // Where to write the output bytes. Suggest to use the StorageFileType names (e.g. flag.map). .arg( Arg::new("output-file") .long("output-file") .required(true) .action(ArgAction::Set), ) // Input file should be json. .arg( Arg::new("input-file").long("input-file").required(true).action(ArgAction::Set), ) .arg( Arg::new("type") .long("type") .required(true) .value_parser(|s: &str| StorageFileType::try_from(s)), ), ) } fn print_storage_file( file_path: &str, file_type: &StorageFileType, as_json: bool, ) -> Result<(), AconfigStorageError> { let bytes = read_file_to_bytes(file_path)?; match file_type { StorageFileType::PackageMap => { let package_table = PackageTable::from_bytes(&bytes)?; println!("{}", to_print_format(package_table, as_json)); } StorageFileType::FlagMap => { let flag_table = FlagTable::from_bytes(&bytes)?; println!("{}", to_print_format(flag_table, as_json)); } StorageFileType::FlagVal => { let flag_value = FlagValueList::from_bytes(&bytes)?; println!("{}", to_print_format(flag_value, as_json)); } StorageFileType::FlagInfo => { let flag_info = FlagInfoList::from_bytes(&bytes)?; println!("{}", to_print_format(flag_info, as_json)); } } Ok(()) } fn to_print_format(file_contents: T, as_json: bool) -> String where T: Serialize + fmt::Debug, { if as_json { serde_json::to_string(&file_contents).unwrap() } else { format!("{:?}", file_contents) } } fn main() -> Result<(), AconfigStorageError> { let matches = cli().get_matches(); match matches.subcommand() { Some(("print", sub_matches)) => { let file_path = sub_matches.get_one::("file").unwrap(); let file_type = sub_matches.get_one::("type").unwrap(); let format = sub_matches.get_one::("format"); let as_json: bool = format == Some(&"json".to_string()); print_storage_file(file_path, file_type, as_json)? } Some(("list", sub_matches)) => { let package_map = sub_matches.get_one::("package-map").unwrap(); let flag_map = sub_matches.get_one::("flag-map").unwrap(); let flag_val = sub_matches.get_one::("flag-val").unwrap(); let flag_info = sub_matches.get_one::("flag-info"); match flag_info { Some(info_file) => { let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?; for flag in flags.iter() { println!( "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}", flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, flag.is_readwrite, flag.has_server_override, flag.has_local_override, ); } } None => { let flags = list_flags(package_map, flag_map, flag_val)?; for flag in flags.iter() { println!( "{} {} {} {:?}", flag.package_name, flag.flag_name, flag.flag_value, flag.value_type, ); } } } } // Converts JSON of the file into raw bytes (as is used on-device). // Intended to generate/easily update these files for testing. Some(("write-bytes", sub_matches)) => { let input_file_path = sub_matches.get_one::("input-file").unwrap(); let input_json = fs::read_to_string(input_file_path).unwrap(); let file_type = sub_matches.get_one::("type").unwrap(); let output_bytes: Vec; match file_type { StorageFileType::FlagVal => { let list: FlagValueList = serde_json::from_str(&input_json).unwrap(); output_bytes = list.into_bytes(); } StorageFileType::FlagInfo => { let list: FlagInfoList = serde_json::from_str(&input_json).unwrap(); output_bytes = list.into_bytes(); } StorageFileType::FlagMap => { let table: FlagTable = serde_json::from_str(&input_json).unwrap(); output_bytes = table.into_bytes(); } StorageFileType::PackageMap => { let table: PackageTable = serde_json::from_str(&input_json).unwrap(); output_bytes = table.into_bytes(); } } let output_file_path = sub_matches.get_one::("output-file").unwrap(); let file = File::create(output_file_path); if file.is_err() { panic!("can't make file"); } let _ = file.unwrap().write_all(&output_bytes); } _ => unreachable!(), } Ok(()) } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/package_table.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! package table module defines the package table file format and methods for serialization //! and deserialization use crate::{ get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u64_from_bytes, read_u8_from_bytes, }; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; use serde::{Deserialize, Serialize}; use std::fmt; /// Package table header struct #[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTableHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_packages: u32, pub bucket_offset: u32, pub node_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for PackageTableHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Packages: {}, Bucket Offset:{}, Node Offset: {}", self.num_packages, self.bucket_offset, self.node_offset )?; Ok(()) } } impl PackageTableHeader { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { let mut result = Vec::new(); result.extend_from_slice(&self.version.to_le_bytes()); let container_bytes = self.container.as_bytes(); result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(container_bytes); result.extend_from_slice(&self.file_type.to_le_bytes()); result.extend_from_slice(&self.file_size.to_le_bytes()); result.extend_from_slice(&self.num_packages.to_le_bytes()); result.extend_from_slice(&self.bucket_offset.to_le_bytes()); result.extend_from_slice(&self.node_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let mut head = 0; let table = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_packages: read_u32_from_bytes(bytes, &mut head)?, bucket_offset: read_u32_from_bytes(bytes, &mut head)?, node_offset: read_u32_from_bytes(bytes, &mut head)?, }; if table.file_type != StorageFileType::PackageMap as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a package map" ))); } Ok(table) } } /// Package table node struct #[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTableNode { pub package_name: String, pub package_id: u32, pub fingerprint: u64, // The index of the first boolean flag in this aconfig package among all boolean // flags in this container. pub boolean_start_index: u32, pub next_offset: Option, } /// Implement debug print trait for node impl fmt::Debug for PackageTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}", self.package_name, self.package_id, self.fingerprint, self.boolean_start_index, self.next_offset )?; Ok(()) } } impl PackageTableNode { /// Serialize to bytes pub fn into_bytes(&self, version: u32) -> Vec { match version { 1 => Self::into_bytes_v1(self), 2 => Self::into_bytes_v2(self), // TODO(b/316357686): into_bytes should return a Result. _ => Self::into_bytes_v2(&self), } } fn into_bytes_v1(&self) -> Vec { let mut result = Vec::new(); let name_bytes = self.package_name.as_bytes(); result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(name_bytes); result.extend_from_slice(&self.package_id.to_le_bytes()); result.extend_from_slice(&self.boolean_start_index.to_le_bytes()); result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes()); result } fn into_bytes_v2(&self) -> Vec { let mut result = Vec::new(); let name_bytes = self.package_name.as_bytes(); result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(name_bytes); result.extend_from_slice(&self.package_id.to_le_bytes()); result.extend_from_slice(&self.fingerprint.to_le_bytes()); result.extend_from_slice(&self.boolean_start_index.to_le_bytes()); result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes()); result } /// Deserialize from bytes based on file version. pub fn from_bytes(bytes: &[u8], version: u32) -> Result { match version { 1 => Self::from_bytes_v1(bytes), 2 => Self::from_bytes_v2(bytes), _ => { return Err(AconfigStorageError::BytesParseFail(anyhow!( "Binary file is an unsupported version: {}", version ))) } } } fn from_bytes_v1(bytes: &[u8]) -> Result { let mut head = 0; let package_name = read_str_from_bytes(bytes, &mut head)?; let package_id = read_u32_from_bytes(bytes, &mut head)?; // v1 does not have fingerprint, so just set to 0. let fingerprint: u64 = 0; let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?; let next_offset = match read_u32_from_bytes(bytes, &mut head)? { 0 => None, val => Some(val), }; let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset }; Ok(node) } fn from_bytes_v2(bytes: &[u8]) -> Result { let mut head = 0; let package_name = read_str_from_bytes(bytes, &mut head)?; let package_id = read_u32_from_bytes(bytes, &mut head)?; let fingerprint = read_u64_from_bytes(bytes, &mut head)?; let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?; let next_offset = match read_u32_from_bytes(bytes, &mut head)? { 0 => None, val => Some(val), }; let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset }; Ok(node) } /// Get the bucket index for a package table node, defined it here so the /// construction side (aconfig binary) and consumption side (flag read lib) /// use the same method of hashing pub fn find_bucket_index(package: &str, num_buckets: u32) -> u32 { get_bucket_index(package.as_bytes(), num_buckets) } } /// Package table struct #[derive(PartialEq, Serialize, Deserialize)] pub struct PackageTable { pub header: PackageTableHeader, pub buckets: Vec>, pub nodes: Vec, } /// Implement debug print trait for package table impl fmt::Debug for PackageTable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Buckets:")?; writeln!(f, "{:?}", self.buckets)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } impl PackageTable { /// Serialize to bytes pub fn into_bytes(&self) -> Vec { [ self.header.into_bytes(), self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::>().concat(), self.nodes .iter() .map(|v| v.into_bytes(self.header.version)) .collect::>() .concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result { let header = PackageTableHeader::from_bytes(bytes)?; let num_packages = header.num_packages; let num_buckets = crate::get_table_size(num_packages)?; let mut head = header.into_bytes().len(); let buckets = (0..num_buckets) .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() { 0 => None, val => Some(val), }) .collect(); let nodes = (0..num_packages) .map(|_| { let node = PackageTableNode::from_bytes(&bytes[head..], header.version)?; head += node.into_bytes(header.version).len(); Ok(node) }) .collect::, AconfigStorageError>>() .map_err(|errmsg| { AconfigStorageError::BytesParseFail(anyhow!( "fail to parse package table: {}", errmsg )) })?; let table = Self { header, buckets, nodes }; Ok(table) } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::create_test_package_table; use crate::{read_u32_from_start_of_bytes, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION}; #[test] // this test point locks down the table serialization fn test_serialization() { for file_version in 1..=MAX_SUPPORTED_FILE_VERSION { let package_table = create_test_package_table(file_version); let header: &PackageTableHeader = &package_table.header; let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let nodes: &Vec = &package_table.nodes; for node in nodes.iter() { let reinterpreted_node = PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version) .unwrap(); assert_eq!(node, &reinterpreted_node); } let package_table_bytes = package_table.into_bytes(); let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes); assert!(reinterpreted_table.is_ok()); assert_eq!(&package_table, &reinterpreted_table.unwrap()); assert_eq!(package_table_bytes.len() as u32, header.file_size); } } #[test] // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { let package_table = create_test_package_table(DEFAULT_FILE_VERSION); let bytes = &package_table.into_bytes(); let unpacked_version = read_u32_from_start_of_bytes(bytes).unwrap(); assert_eq!(unpacked_version, DEFAULT_FILE_VERSION); } #[test] fn test_round_trip_default() { let table: PackageTable = create_test_package_table(DEFAULT_FILE_VERSION); let table_bytes = table.into_bytes(); let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap(); assert_eq!(table, reinterpreted_table); } #[test] fn test_round_trip_max() { let table: PackageTable = create_test_package_table(MAX_SUPPORTED_FILE_VERSION); let table_bytes = table.into_bytes(); let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap(); assert_eq!(table, reinterpreted_table); } #[test] // this test point locks down file type check fn test_file_type_check() { let mut package_table = create_test_package_table(DEFAULT_FILE_VERSION); package_table.header.file_type = 123u8; let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a package map)") ); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/protos.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // When building with the Android tool-chain // // - an external crate `aconfig_storage_metadata_protos` will be generated // - the feature "cargo" will be disabled // // When building with cargo // // - a local sub-module will be generated in OUT_DIR and included in this file // - the feature "cargo" will be enabled // // This module hides these differences from the rest of the codebase. // ---- When building with the Android tool-chain ---- #[cfg(not(feature = "cargo"))] mod auto_generated { pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage; pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo; pub use ProtoStorage::Storage_files as ProtoStorageFiles; } // ---- When building with cargo ---- #[cfg(feature = "cargo")] mod auto_generated { // include! statements should be avoided (because they import file contents verbatim), but // because this is only used during local development, and only if using cargo instead of the // Android tool-chain, we allow it include!(concat!(env!("OUT_DIR"), "/aconfig_storage_protos/mod.rs")); pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo; pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles; } // ---- Common for both the Android tool-chain and cargo ---- pub use auto_generated::*; use anyhow::Result; use protobuf::Message; use std::io::Write; use tempfile::NamedTempFile; pub mod storage_record_pb { use super::*; use anyhow::ensure; pub fn try_from_binary_proto(bytes: &[u8]) -> Result { let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?; verify_fields(&message)?; Ok(message) } pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> { for storage_file_info in storage_files.files.iter() { ensure!( !storage_file_info.package_map().is_empty(), "invalid storage file record: missing package map file for container {}", storage_file_info.container() ); ensure!( !storage_file_info.flag_map().is_empty(), "invalid storage file record: missing flag map file for container {}", storage_file_info.container() ); ensure!( !storage_file_info.flag_val().is_empty(), "invalid storage file record: missing flag val file for container {}", storage_file_info.container() ); } Ok(()) } pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result> { let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?; let mut binary_proto = Vec::new(); storage_files.write_to_vec(&mut binary_proto)?; Ok(binary_proto) } pub fn write_proto_to_temp_file(text_proto: &str) -> Result { let bytes = get_binary_proto_from_text_proto(text_proto).unwrap(); let mut file = NamedTempFile::new()?; let _ = file.write_all(&bytes); Ok(file) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_storage_record_pb() { let text_proto = r#" files { version: 0 container: "system" package_map: "/system/etc/package.map" flag_map: "/system/etc/flag.map" flag_val: "/metadata/aconfig/system.val" timestamp: 12345 } files { version: 1 container: "product" package_map: "/product/etc/package.map" flag_map: "/product/etc/flag.map" flag_val: "/metadata/aconfig/product.val" timestamp: 54321 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap(); assert_eq!(storage_files.files.len(), 2); let system_file = &storage_files.files[0]; assert_eq!(system_file.version(), 0); assert_eq!(system_file.container(), "system"); assert_eq!(system_file.package_map(), "/system/etc/package.map"); assert_eq!(system_file.flag_map(), "/system/etc/flag.map"); assert_eq!(system_file.flag_val(), "/metadata/aconfig/system.val"); assert_eq!(system_file.timestamp(), 12345); let product_file = &storage_files.files[1]; assert_eq!(product_file.version(), 1); assert_eq!(product_file.container(), "product"); assert_eq!(product_file.package_map(), "/product/etc/package.map"); assert_eq!(product_file.flag_map(), "/product/etc/flag.map"); assert_eq!(product_file.flag_val(), "/metadata/aconfig/product.val"); assert_eq!(product_file.timestamp(), 54321); } #[test] fn test_parse_invalid_storage_record_pb() { let text_proto = r#" files { version: 0 container: "system" package_map: "" flag_map: "/system/etc/flag.map" flag_val: "/metadata/aconfig/system.val" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing package map file for container system" ); let text_proto = r#" files { version: 0 container: "system" package_map: "/system/etc/package.map" flag_map: "" flag_val: "/metadata/aconfig/system.val" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing flag map file for container system" ); let text_proto = r#" files { version: 0 container: "system" package_map: "/system/etc/package.map" flag_map: "/system/etc/flag.map" flag_val: "" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing flag val file for container system" ); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/sip_hasher13.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! An implementation of SipHash13 use std::cmp; use std::mem; use std::ptr; use std::slice; use std::hash::Hasher; /// An implementation of SipHash 2-4. /// #[derive(Debug, Clone, Default)] pub struct SipHasher13 { k0: u64, k1: u64, length: usize, // how many bytes we've processed state: State, // hash State tail: u64, // unprocessed bytes le ntail: usize, // how many bytes in tail are valid } #[derive(Debug, Clone, Copy, Default)] #[repr(C)] struct State { // v0, v2 and v1, v3 show up in pairs in the algorithm, // and simd implementations of SipHash will use vectors // of v02 and v13. By placing them in this order in the struct, // the compiler can pick up on just a few simd optimizations by itself. v0: u64, v2: u64, v1: u64, v3: u64, } macro_rules! compress { ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }}; ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{ $v0 = $v0.wrapping_add($v1); $v1 = $v1.rotate_left(13); $v1 ^= $v0; $v0 = $v0.rotate_left(32); $v2 = $v2.wrapping_add($v3); $v3 = $v3.rotate_left(16); $v3 ^= $v2; $v0 = $v0.wrapping_add($v3); $v3 = $v3.rotate_left(21); $v3 ^= $v0; $v2 = $v2.wrapping_add($v1); $v1 = $v1.rotate_left(17); $v1 ^= $v2; $v2 = $v2.rotate_left(32); }}; } /// Load an integer of the desired type from a byte stream, in LE order. Uses /// `copy_nonoverlapping` to let the compiler generate the most efficient way /// to load it from a possibly unaligned address. /// /// Unsafe because: unchecked indexing at i..i+size_of(int_ty) macro_rules! load_int_le { ($buf:expr, $i:expr, $int_ty:ident) => {{ debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); let mut data = 0 as $int_ty; ptr::copy_nonoverlapping( $buf.get_unchecked($i), &mut data as *mut _ as *mut u8, mem::size_of::<$int_ty>(), ); data.to_le() }}; } /// Load an u64 using up to 7 bytes of a byte slice. /// /// Unsafe because: unchecked indexing at start..start+len #[inline] unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { debug_assert!(len < 8); let mut i = 0; // current byte index (from LSB) in the output u64 let mut out = 0; if i + 3 < len { out = load_int_le!(buf, start + i, u32) as u64; i += 4; } if i + 1 < len { out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8); i += 2 } if i < len { out |= (*buf.get_unchecked(start + i) as u64) << (i * 8); i += 1; } debug_assert_eq!(i, len); out } impl SipHasher13 { /// Creates a new `SipHasher13` with the two initial keys set to 0. #[inline] pub fn new() -> SipHasher13 { SipHasher13::new_with_keys(0, 0) } /// Creates a `SipHasher13` that is keyed off the provided keys. #[inline] pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { let mut sip_hasher = SipHasher13 { k0: key0, k1: key1, length: 0, state: State { v0: 0, v1: 0, v2: 0, v3: 0 }, tail: 0, ntail: 0, }; sip_hasher.reset(); sip_hasher } #[inline] fn c_rounds(state: &mut State) { compress!(state); } #[inline] fn d_rounds(state: &mut State) { compress!(state); compress!(state); compress!(state); } #[inline] fn reset(&mut self) { self.length = 0; self.state.v0 = self.k0 ^ 0x736f6d6570736575; self.state.v1 = self.k1 ^ 0x646f72616e646f6d; self.state.v2 = self.k0 ^ 0x6c7967656e657261; self.state.v3 = self.k1 ^ 0x7465646279746573; self.ntail = 0; } // Specialized write function that is only valid for buffers with len <= 8. // It's used to force inlining of write_u8 and write_usize, those would normally be inlined // except for composite types (that includes slices and str hashing because of delimiter). // Without this extra push the compiler is very reluctant to inline delimiter writes, // degrading performance substantially for the most common use cases. #[inline] fn short_write(&mut self, msg: &[u8]) { debug_assert!(msg.len() <= 8); let length = msg.len(); self.length += length; let needed = 8 - self.ntail; let fill = cmp::min(length, needed); if fill == 8 { // safe to call since msg hasn't been loaded self.tail = unsafe { load_int_le!(msg, 0, u64) }; } else { // safe to call since msg hasn't been loaded, and fill <= msg.len() self.tail |= unsafe { u8to64_le(msg, 0, fill) } << (8 * self.ntail); if length < needed { self.ntail += length; return; } } self.state.v3 ^= self.tail; Self::c_rounds(&mut self.state); self.state.v0 ^= self.tail; // Buffered tail is now flushed, process new input. self.ntail = length - needed; // safe to call since number of `needed` bytes has been loaded // and self.ntail + needed == msg.len() self.tail = unsafe { u8to64_le(msg, needed, self.ntail) }; } } impl Hasher for SipHasher13 { // see short_write comment for explanation #[inline] fn write_usize(&mut self, i: usize) { // safe to call, since convert the pointer to u8 let bytes = unsafe { slice::from_raw_parts(&i as *const usize as *const u8, mem::size_of::()) }; self.short_write(bytes); } // see short_write comment for explanation #[inline] fn write_u8(&mut self, i: u8) { self.short_write(&[i]); } #[inline] fn write(&mut self, msg: &[u8]) { let length = msg.len(); self.length += length; let mut needed = 0; // loading unprocessed byte from last write if self.ntail != 0 { needed = 8 - self.ntail; // safe to call, since msg hasn't been processed // and cmp::min(length, needed) < 8 self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << 8 * self.ntail; if length < needed { self.ntail += length; return; } else { self.state.v3 ^= self.tail; Self::c_rounds(&mut self.state); self.state.v0 ^= self.tail; self.ntail = 0; } } // Buffered tail is now flushed, process new input. let len = length - needed; let left = len & 0x7; let mut i = needed; while i < len - left { // safe to call since if i < len - left, it means msg has at least 1 byte to load let mi = unsafe { load_int_le!(msg, i, u64) }; self.state.v3 ^= mi; Self::c_rounds(&mut self.state); self.state.v0 ^= mi; i += 8; } // safe to call since if left == 0, since this call will load nothing // if left > 0, it means there are number of `left` bytes in msg self.tail = unsafe { u8to64_le(msg, i, left) }; self.ntail = left; } #[inline] fn finish(&self) -> u64 { let mut state = self.state; let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; state.v3 ^= b; Self::c_rounds(&mut state); state.v0 ^= b; state.v2 ^= 0xff; Self::d_rounds(&mut state); state.v0 ^ state.v1 ^ state.v2 ^ state.v3 } } #[cfg(test)] mod tests { use super::*; use std::hash::{Hash, Hasher}; use std::string::String; #[test] // this test point locks down the value list serialization fn test_sip_hash13_string_hash() { let mut sip_hash13 = SipHasher13::new(); let test_str1 = String::from("com.google.android.test"); test_str1.hash(&mut sip_hash13); assert_eq!(17898838669067067585, sip_hash13.finish()); let test_str2 = String::from("adfadfadf adfafadadf 1231241241"); test_str2.hash(&mut sip_hash13); assert_eq!(13543518987672889310, sip_hash13.finish()); } #[test] fn test_sip_hash13_write() { let mut sip_hash13 = SipHasher13::new(); let test_str1 = String::from("com.google.android.test"); sip_hash13.write(test_str1.as_bytes()); sip_hash13.write_u8(0xff); assert_eq!(17898838669067067585, sip_hash13.finish()); let mut sip_hash132 = SipHasher13::new(); let test_str1 = String::from("com.google.android.test"); sip_hash132.write(test_str1.as_bytes()); assert_eq!(9685440969685209025, sip_hash132.finish()); sip_hash132.write(test_str1.as_bytes()); assert_eq!(6719694176662736568, sip_hash132.finish()); let mut sip_hash133 = SipHasher13::new(); let test_str2 = String::from("abcdefg"); test_str2.hash(&mut sip_hash133); assert_eq!(2492161047327640297, sip_hash133.finish()); let mut sip_hash134 = SipHasher13::new(); let test_str3 = String::from("abcdefgh"); test_str3.hash(&mut sip_hash134); assert_eq!(6689927370435554326, sip_hash134.finish()); } #[test] fn test_sip_hash13_write_short() { let mut sip_hash13 = SipHasher13::new(); sip_hash13.write_u8(0x61); assert_eq!(4644417185603328019, sip_hash13.finish()); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/src/test_utils.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode}; use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; use crate::flag_value::{FlagValueHeader, FlagValueList}; use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; use crate::{AconfigStorageError, StorageFileType, StoredFlagType}; use anyhow::anyhow; use std::io::Write; use tempfile::NamedTempFile; pub fn create_test_package_table(version: u32) -> PackageTable { let header = PackageTableHeader { version: version, container: String::from("mockup"), file_type: StorageFileType::PackageMap as u8, file_size: match version { 1 => 209, 2 => 233, _ => panic!("Unsupported version."), }, num_packages: 3, bucket_offset: 31, node_offset: 59, }; let buckets: Vec> = match version { 1 => vec![Some(59), None, None, Some(109), None, None, None], 2 => vec![Some(59), None, None, Some(117), None, None, None], _ => panic!("Unsupported version."), }; let first_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_2"), package_id: 1, fingerprint: match version { 1 => 0, 2 => 4431940502274857964u64, _ => panic!("Unsupported version."), }, boolean_start_index: 3, next_offset: None, }; let second_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_1"), package_id: 0, fingerprint: match version { 1 => 0, 2 => 15248948510590158086u64, _ => panic!("Unsupported version."), }, boolean_start_index: 0, next_offset: match version { 1 => Some(159), 2 => Some(175), _ => panic!("Unsupported version."), }, }; let third_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_4"), package_id: 2, fingerprint: match version { 1 => 0, 2 => 16233229917711622375u64, _ => panic!("Unsupported version."), }, boolean_start_index: 6, next_offset: None, }; let nodes = vec![first_node, second_node, third_node]; PackageTable { header, buckets, nodes } } impl FlagTableNode { // create test baseline, syntactic sugar fn new_expected( package_id: u32, flag_name: &str, flag_type: u16, flag_index: u16, next_offset: Option, ) -> Self { Self { package_id, flag_name: flag_name.to_string(), flag_type: StoredFlagType::try_from(flag_type).unwrap(), flag_index, next_offset, } } } pub fn create_test_flag_table(version: u32) -> FlagTable { let header = FlagTableHeader { version: version, container: String::from("mockup"), file_type: StorageFileType::FlagMap as u8, file_size: 321, num_flags: 8, bucket_offset: 31, node_offset: 99, }; let buckets: Vec> = vec![ Some(99), Some(125), None, None, None, None, Some(177), Some(204), None, Some(262), None, None, None, None, None, Some(294), None, ]; let nodes = vec![ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None), FlagTableNode::new_expected(0, "enabled_rw", 0, 2, Some(151)), FlagTableNode::new_expected(2, "enabled_rw", 0, 1, None), FlagTableNode::new_expected(1, "disabled_rw", 0, 0, None), FlagTableNode::new_expected(1, "enabled_fixed_ro", 2, 1, Some(236)), FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None), FlagTableNode::new_expected(2, "enabled_fixed_ro", 2, 0, None), FlagTableNode::new_expected(0, "disabled_rw", 0, 0, None), ]; FlagTable { header, buckets, nodes } } pub fn create_test_flag_value_list(version: u32) -> FlagValueList { let header = FlagValueHeader { version: version, container: String::from("mockup"), file_type: StorageFileType::FlagVal as u8, file_size: 35, num_flags: 8, boolean_value_offset: 27, }; let booleans: Vec = vec![false, true, true, false, true, true, true, true]; FlagValueList { header, booleans } } pub fn create_test_flag_info_list(version: u32) -> FlagInfoList { let header = FlagInfoHeader { version: version, container: String::from("mockup"), file_type: StorageFileType::FlagInfo as u8, file_size: 35, num_flags: 8, boolean_flag_offset: 27, }; let is_flag_rw = [true, false, true, true, false, false, false, true]; let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(); FlagInfoList { header, nodes } } pub fn write_bytes_to_temp_file(bytes: &[u8]) -> Result { let mut file = NamedTempFile::new().map_err(|_| { AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file")) })?; let _ = file.write_all(&bytes); Ok(file) } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; /** * Exception thrown when an error occurs while accessing Aconfig Storage. * *

This exception indicates a general problem with Aconfig Storage, such as an inability to read * or write data. */ public class AconfigStorageException extends RuntimeException { /** Generic error code indicating an unspecified Aconfig Storage error. */ public static final int ERROR_GENERIC = 0; /** Error code indicating that the Aconfig Storage system is not found on the device. */ public static final int ERROR_STORAGE_SYSTEM_NOT_FOUND = 1; /** Error code indicating that the requested configuration package is not found. */ public static final int ERROR_PACKAGE_NOT_FOUND = 2; /** Error code indicating that the specified container is not found. */ public static final int ERROR_CONTAINER_NOT_FOUND = 3; /** Error code indicating that there was an error reading the Aconfig Storage file. */ public static final int ERROR_CANNOT_READ_STORAGE_FILE = 4; public static final int ERROR_FILE_FINGERPRINT_MISMATCH = 5; private final int mErrorCode; /** * Constructs a new {@code AconfigStorageException} with a generic error code and the specified * detail message. * * @param msg The detail message for this exception. */ public AconfigStorageException(String msg) { super(msg); mErrorCode = ERROR_GENERIC; } /** * Constructs a new {@code AconfigStorageException} with a generic error code, the specified * detail message, and cause. * * @param msg The detail message for this exception. * @param cause The cause of this exception. */ public AconfigStorageException(String msg, Throwable cause) { super(msg, cause); mErrorCode = ERROR_GENERIC; } /** * Constructs a new {@code AconfigStorageException} with the specified error code and detail * message. * * @param errorCode The error code for this exception. * @param msg The detail message for this exception. */ public AconfigStorageException(int errorCode, String msg) { super(msg); mErrorCode = errorCode; } /** * Constructs a new {@code AconfigStorageException} with the specified error code, detail * message, and cause. * * @param errorCode The error code for this exception. * @param msg The detail message for this exception. * @param cause The cause of this exception. */ public AconfigStorageException(int errorCode, String msg, Throwable cause) { super(msg, cause); mErrorCode = errorCode; } /** * Returns the error code associated with this exception. * * @return The error code. */ public int getErrorCode() { return mErrorCode; } /** * Returns the error message for this exception, including the error code and the original * message. * * @return The error message. */ @Override public String getMessage() { return errorString() + ": " + super.getMessage(); } /** * Returns a string representation of the error code. * * @return The error code string. */ private String errorString() { switch (mErrorCode) { case ERROR_GENERIC: return "ERROR_GENERIC"; case ERROR_STORAGE_SYSTEM_NOT_FOUND: return "ERROR_STORAGE_SYSTEM_NOT_FOUND"; case ERROR_PACKAGE_NOT_FOUND: return "ERROR_PACKAGE_NOT_FOUND"; case ERROR_CONTAINER_NOT_FOUND: return "ERROR_CONTAINER_NOT_FOUND"; case ERROR_CANNOT_READ_STORAGE_FILE: return "ERROR_CANNOT_READ_STORAGE_FILE"; case ERROR_FILE_FINGERPRINT_MISMATCH: return "ERROR_FILE_FINGERPRINT_MISMATCH"; default: return ""; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.Objects; public class ByteBufferReader { private ByteBuffer mByteBuffer; private int mPosition; public ByteBufferReader(ByteBuffer byteBuffer) { this.mByteBuffer = byteBuffer; this.mByteBuffer.order(ByteOrder.LITTLE_ENDIAN); } public int readByte() { return Byte.toUnsignedInt(mByteBuffer.get(nextGetIndex(1))); } public int readShort() { return Short.toUnsignedInt(mByteBuffer.getShort(nextGetIndex(2))); } public int readInt() { return this.mByteBuffer.getInt(nextGetIndex(4)); } public long readLong() { return this.mByteBuffer.getLong(nextGetIndex(8)); } public String readString() { int length = readInt(); if (length > 1024) { throw new AconfigStorageException( "String length exceeds maximum allowed size (1024 bytes): " + length); } byte[] bytes = new byte[length]; getArray(nextGetIndex(length), bytes, 0, length); return new String(bytes, StandardCharsets.UTF_8); } public int readByte(int i) { return Byte.toUnsignedInt(mByteBuffer.get(i)); } public void position(int newPosition) { mPosition = newPosition; } public int position() { return mPosition; } private int nextGetIndex(int nb) { int p = mPosition; mPosition += nb; return p; } private void getArray(int index, byte[] dst, int offset, int length) { Objects.checkFromIndexSize(index, length, mByteBuffer.limit()); Objects.checkFromIndexSize(offset, length, dst.length); int end = offset + length; for (int i = offset, j = index; i < end; i++, j++) { dst[i] = mByteBuffer.get(j); } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; public enum FileType { PACKAGE_MAP(0), FLAG_MAP(1), FLAG_VAL(2), FLAG_INFO(3); public final int type; FileType(int type) { this.type = type; } public static FileType fromInt(int index) { switch (index) { case 0: return PACKAGE_MAP; case 1: return FLAG_MAP; case 2: return FLAG_VAL; case 3: return FLAG_INFO; default: return null; } } @Override public String toString() { switch (type) { case 0: return "PACKAGE_MAP"; case 1: return "FLAG_MAP"; case 2: return "FLAG_VAL"; case 3: return "FLAG_INFO"; default: return "unrecognized type"; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.ByteBuffer; import java.util.Objects; public class FlagTable { private Header mHeader; private ByteBuffer mBuffer; public static FlagTable fromBytes(ByteBuffer bytes) { FlagTable flagTable = new FlagTable(); flagTable.mBuffer = bytes; flagTable.mHeader = Header.fromBytes(new ByteBufferReader(bytes)); return flagTable; } public Node get(int packageId, String flagName) { int numBuckets = (mHeader.mNodeOffset - mHeader.mBucketOffset) / 4; int bucketIndex = TableUtils.getBucketIndex(makeKey(packageId, flagName), numBuckets); int newPosition = mHeader.mBucketOffset + bucketIndex * 4; if (newPosition >= mHeader.mNodeOffset) { return null; } ByteBufferReader reader = new ByteBufferReader(mBuffer) ; reader.position(newPosition); int nodeIndex = reader.readInt(); if (nodeIndex < mHeader.mNodeOffset || nodeIndex >= mHeader.mFileSize) { return null; } while (nodeIndex != -1) { reader.position(nodeIndex); Node node = Node.fromBytes(reader); if (Objects.equals(flagName, node.mFlagName) && packageId == node.mPackageId) { return node; } nodeIndex = node.mNextOffset; } return null; } public Header getHeader() { return mHeader; } private static byte[] makeKey(int packageId, String flagName) { StringBuilder ret = new StringBuilder(); return ret.append(packageId).append('/').append(flagName).toString().getBytes(UTF_8); } public static class Header { private int mVersion; private String mContainer; private FileType mFileType; private int mFileSize; private int mNumFlags; private int mBucketOffset; private int mNodeOffset; public static Header fromBytes(ByteBufferReader reader) { Header header = new Header(); header.mVersion = reader.readInt(); header.mContainer = reader.readString(); header.mFileType = FileType.fromInt(reader.readByte()); header.mFileSize = reader.readInt(); header.mNumFlags = reader.readInt(); header.mBucketOffset = reader.readInt(); header.mNodeOffset = reader.readInt(); if (header.mFileType != FileType.FLAG_MAP) { throw new AconfigStorageException("binary file is not a flag map"); } return header; } public int getVersion() { return mVersion; } public String getContainer() { return mContainer; } public FileType getFileType() { return mFileType; } public int getFileSize() { return mFileSize; } public int getNumFlags() { return mNumFlags; } public int getBucketOffset() { return mBucketOffset; } public int getNodeOffset() { return mNodeOffset; } } public static class Node { private String mFlagName; private FlagType mFlagType; private int mPackageId; private int mFlagIndex; private int mNextOffset; public static Node fromBytes(ByteBufferReader reader) { Node node = new Node(); node.mPackageId = reader.readInt(); node.mFlagName = reader.readString(); node.mFlagType = FlagType.fromInt(reader.readShort()); node.mFlagIndex = reader.readShort(); node.mNextOffset = reader.readInt(); node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset; return node; } @Override public int hashCode() { return Objects.hash(mFlagName, mFlagType, mPackageId, mFlagIndex, mNextOffset); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || !(obj instanceof Node)) { return false; } Node other = (Node) obj; return Objects.equals(mFlagName, other.mFlagName) && Objects.equals(mFlagType, other.mFlagType) && mPackageId == other.mPackageId && mFlagIndex == other.mFlagIndex && mNextOffset == other.mNextOffset; } public String getFlagName() { return mFlagName; } public FlagType getFlagType() { return mFlagType; } public int getPackageId() { return mPackageId; } public int getFlagIndex() { return mFlagIndex; } public int getNextOffset() { return mNextOffset; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; public enum FlagType { ReadWriteBoolean (0), ReadOnlyBoolean(1), FixedReadOnlyBoolean(2); public final int type; FlagType(int type) { this.type = type; } public static FlagType fromInt(int index) { switch (index) { case 0: return ReadWriteBoolean; case 1: return ReadOnlyBoolean; case 2: return FixedReadOnlyBoolean; default: return null; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import java.nio.ByteBuffer; public class FlagValueList { private Header mHeader; private ByteBufferReader mReader; public static FlagValueList fromBytes(ByteBuffer bytes) { FlagValueList flagValueList = new FlagValueList(); flagValueList.mReader = new ByteBufferReader(bytes); flagValueList.mHeader = Header.fromBytes(flagValueList.mReader); return flagValueList; } public boolean getBoolean(int index) { return mReader.readByte(mHeader.mBooleanValueOffset + index) == 1; } public Header getHeader() { return mHeader; } public int size() { return mHeader.mNumFlags; } public static class Header { private int mVersion; private String mContainer; private FileType mFileType; private int mFileSize; private int mNumFlags; private int mBooleanValueOffset; public static Header fromBytes(ByteBufferReader reader) { Header header = new Header(); header.mVersion = reader.readInt(); header.mContainer = reader.readString(); header.mFileType = FileType.fromInt(reader.readByte()); header.mFileSize = reader.readInt(); header.mNumFlags = reader.readInt(); header.mBooleanValueOffset = reader.readInt(); if (header.mFileType != FileType.FLAG_VAL) { throw new AconfigStorageException("binary file is not a flag value file"); } return header; } public int getVersion() { return mVersion; } public String getContainer() { return mContainer; } public FileType getFileType() { return mFileType; } public int getFileSize() { return mFileSize; } public int getNumFlags() { return mNumFlags; } public int getBooleanValueOffset() { return mBooleanValueOffset; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class PackageTable { private static final int FINGERPRINT_BYTES = 8; // int: mPackageId + int: mBooleanStartIndex + int: mNextOffset private static final int NODE_SKIP_BYTES = 12; private Header mHeader; private ByteBuffer mBuffer; public static PackageTable fromBytes(ByteBuffer bytes) { PackageTable packageTable = new PackageTable(); packageTable.mBuffer = bytes; packageTable.mHeader = Header.fromBytes(new ByteBufferReader(bytes)); return packageTable; } public Node get(String packageName) { int numBuckets = (mHeader.mNodeOffset - mHeader.mBucketOffset) / 4; int bucketIndex = TableUtils.getBucketIndex(packageName.getBytes(UTF_8), numBuckets); int newPosition = mHeader.mBucketOffset + bucketIndex * 4; if (newPosition >= mHeader.mNodeOffset) { return null; } ByteBufferReader reader = new ByteBufferReader(mBuffer); reader.position(newPosition); int nodeIndex = reader.readInt(); if (nodeIndex < mHeader.mNodeOffset || nodeIndex >= mHeader.mFileSize) { return null; } while (nodeIndex != -1) { reader.position(nodeIndex); Node node = Node.fromBytes(reader, mHeader.mVersion); if (Objects.equals(packageName, node.mPackageName)) { return node; } nodeIndex = node.mNextOffset; } return null; } public List getPackageList() { List list = new ArrayList<>(mHeader.mNumPackages); ByteBufferReader reader = new ByteBufferReader(mBuffer); reader.position(mHeader.mNodeOffset); int fingerprintBytes = mHeader.mVersion == 1 ? 0 : FINGERPRINT_BYTES; int skipBytes = fingerprintBytes + NODE_SKIP_BYTES; for (int i = 0; i < mHeader.mNumPackages; i++) { list.add(reader.readString()); reader.position(reader.position() + skipBytes); } return list; } public Header getHeader() { return mHeader; } public static class Header { private int mVersion; private String mContainer; private FileType mFileType; private int mFileSize; private int mNumPackages; private int mBucketOffset; private int mNodeOffset; private static Header fromBytes(ByteBufferReader reader) { Header header = new Header(); header.mVersion = reader.readInt(); header.mContainer = reader.readString(); header.mFileType = FileType.fromInt(reader.readByte()); header.mFileSize = reader.readInt(); header.mNumPackages = reader.readInt(); header.mBucketOffset = reader.readInt(); header.mNodeOffset = reader.readInt(); if (header.mFileType != FileType.PACKAGE_MAP) { throw new AconfigStorageException("binary file is not a package map"); } return header; } public int getVersion() { return mVersion; } public String getContainer() { return mContainer; } public FileType getFileType() { return mFileType; } public int getFileSize() { return mFileSize; } public int getNumPackages() { return mNumPackages; } public int getBucketOffset() { return mBucketOffset; } public int getNodeOffset() { return mNodeOffset; } } public static class Node { private String mPackageName; private int mPackageId; private long mPackageFingerprint; private int mBooleanStartIndex; private int mNextOffset; private boolean mHasPackageFingerprint; private static Node fromBytes(ByteBufferReader reader, int version) { switch (version) { case 1: return fromBytesV1(reader); case 2: return fromBytesV2(reader); default: // Do we want to throw here? return new Node(); } } private static Node fromBytesV1(ByteBufferReader reader) { Node node = new Node(); node.mPackageName = reader.readString(); node.mPackageId = reader.readInt(); node.mBooleanStartIndex = reader.readInt(); node.mNextOffset = reader.readInt(); node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset; return node; } private static Node fromBytesV2(ByteBufferReader reader) { Node node = new Node(); node.mPackageName = reader.readString(); node.mPackageId = reader.readInt(); node.mPackageFingerprint = reader.readLong(); node.mBooleanStartIndex = reader.readInt(); node.mNextOffset = reader.readInt(); node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset; node.mHasPackageFingerprint = true; return node; } @Override public int hashCode() { return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || !(obj instanceof Node)) { return false; } Node other = (Node) obj; return Objects.equals(mPackageName, other.mPackageName) && mPackageId == other.mPackageId && mBooleanStartIndex == other.mBooleanStartIndex && mNextOffset == other.mNextOffset; } public String getPackageName() { return mPackageName; } public int getPackageId() { return mPackageId; } public long getPackageFingerprint() { return mPackageFingerprint; } public int getBooleanStartIndex() { return mBooleanStartIndex; } public int getNextOffset() { return mNextOffset; } public boolean hasPackageFingerprint() { return mHasPackageFingerprint; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/SipHasher13.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; public class SipHasher13 { static class State { private long v0; private long v2; private long v1; private long v3; public State(long k0, long k1) { v0 = k0 ^ 0x736f6d6570736575L; v1 = k1 ^ 0x646f72616e646f6dL; v2 = k0 ^ 0x6c7967656e657261L; v3 = k1 ^ 0x7465646279746573L; } public void compress(long m) { v3 ^= m; cRounds(); v0 ^= m; } public long finish() { v2 ^= 0xff; dRounds(); return v0 ^ v1 ^ v2 ^ v3; } private void cRounds() { v0 += v1; v1 = Long.rotateLeft(v1, 13); v1 ^= v0; v0 = Long.rotateLeft(v0, 32); v2 += v3; v3 = Long.rotateLeft(v3, 16); v3 ^= v2; v0 += v3; v3 = Long.rotateLeft(v3, 21); v3 ^= v0; v2 += v1; v1 = Long.rotateLeft(v1, 17); v1 ^= v2; v2 = Long.rotateLeft(v2, 32); } private void dRounds() { for (int i = 0; i < 3; i++) { v0 += v1; v1 = Long.rotateLeft(v1, 13); v1 ^= v0; v0 = Long.rotateLeft(v0, 32); v2 += v3; v3 = Long.rotateLeft(v3, 16); v3 ^= v2; v0 += v3; v3 = Long.rotateLeft(v3, 21); v3 ^= v0; v2 += v1; v1 = Long.rotateLeft(v1, 17); v1 ^= v2; v2 = Long.rotateLeft(v2, 32); } } } public static long hash(byte[] data) { State state = new State(0, 0); int len = data.length; int left = len & 0x7; int index = 0; while (index < len - left) { long mi = loadLe(data, index, 8); index += 8; state.compress(mi); } // padding the end with 0xff to be consistent with rust long m = (0xffL << (left * 8)) | loadLe(data, index, left); if (left == 0x7) { // compress the m w-2 state.compress(m); m = 0L; } // len adds 1 since padded 0xff m |= (((len + 1) & 0xffL) << 56); state.compress(m); return state.finish(); } private static long loadLe(byte[] data, int offset, int size) { long m = 0; for (int i = 0; i < size; i++) { m |= (data[i + offset] & 0xffL) << (i * 8); } return m; } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/StorageFileProvider.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import java.io.Closeable; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** @hide */ public class StorageFileProvider { private static final String DEFAULT_MAP_PATH = "/metadata/aconfig/maps/"; private static final String DEFAULT_BOOT_PATH = "/metadata/aconfig/boot/"; private static final String PMAP_FILE_EXT = ".package.map"; private static final String FMAP_FILE_EXT = ".flag.map"; private static final String VAL_FILE_EXT = ".val"; private static final StorageFileProvider DEFAULT_INSTANCE = new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH); private final String mMapPath; private final String mBootPath; /** @hide */ public static StorageFileProvider getDefaultProvider() { return DEFAULT_INSTANCE; } /** @hide */ public StorageFileProvider(String mapPath, String bootPath) { mMapPath = mapPath; mBootPath = bootPath; } /** @hide */ public List listContainers(String[] excludes) { List result = new ArrayList<>(); Set set = new HashSet<>(Arrays.asList(excludes)); try { DirectoryStream stream = Files.newDirectoryStream(Paths.get(mMapPath), "*" + PMAP_FILE_EXT); for (Path entry : stream) { String fileName = entry.getFileName().toString(); String container = fileName.substring(0, fileName.length() - PMAP_FILE_EXT.length()); if (!set.contains(container)) { result.add(container); } } } catch (NoSuchFileException e) { return result; } catch (Exception e) { throw new AconfigStorageException( String.format("Fail to list map files in path %s", mMapPath), e); } return result; } /** @hide */ public PackageTable getPackageTable(String container) { return PackageTable.fromBytes( mapStorageFile( Paths.get(mMapPath, container + PMAP_FILE_EXT), FileType.PACKAGE_MAP)); } /** @hide */ public FlagTable getFlagTable(String container) { return FlagTable.fromBytes( mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT), FileType.FLAG_MAP)); } /** @hide */ public FlagValueList getFlagValueList(String container) { return FlagValueList.fromBytes( mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT), FileType.FLAG_VAL)); } // Map a storage file given file path private static MappedByteBuffer mapStorageFile(Path file, FileType type) { FileChannel channel = null; try { channel = FileChannel.open(file, StandardOpenOption.READ); return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); } catch (Exception e) { throw new AconfigStorageException( AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, String.format("Fail to mmap storage %s file %s", type.toString(), file), e); } finally { quietlyDispose(channel); } } private static void quietlyDispose(Closeable closable) { try { if (closable != null) { closable.close(); } } catch (Exception e) { // no need to care, at least as of now } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; public class TableUtils { private static final int[] HASH_PRIMES = new int[] { 7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; public static int getTableSize(int numEntries) { for (int i : HASH_PRIMES) { if (i < 2 * numEntries) continue; return i; } throw new AconfigStorageException("Number of items in a hash table exceeds limit"); } public static int getBucketIndex(byte[] val, int numBuckets) { long hashVal = SipHasher13.hash(val); return (int) Long.remainderUnsigned(hashVal, numBuckets); } public static class StorageFilesBundle { public final PackageTable packageTable; public final FlagTable flagTable; public final FlagValueList flagValueList; public StorageFilesBundle (PackageTable pTable, FlagTable fTable, FlagValueList fValueList) { this.packageTable = pTable; this.flagTable = fTable; this.flagValueList = fValueList; } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/Android.bp ================================================ cc_test { name: "aconfig_storage_file.test.cpp", team: "trendy_team_android_core_experiments", srcs: [ "storage_file_test.cpp", ], static_libs: [ "libgmock", "libaconfig_storage_file_cc", "libbase", ], data: [ "data/v1/package_v1.map", "data/v1/flag_v1.map", "data/v1/flag_v1.val", "data/v1/flag_v1.info", "data/v2/package_v2.map", "data/v2/flag_v2.map", "data/v2/flag_v2.val", "data/v2/flag_v2.info", ], test_suites: [ "device-tests", "general-tests", ], } android_test { name: "aconfig_storage_file.test.java", team: "trendy_team_android_core_experiments", srcs: [ "srcs/**/*.java", ], static_libs: [ "androidx.test.runner", "junit", "aconfig_storage_file_java", ], test_config: "AndroidStorageJaveTest.xml", sdk_version: "test_current", data: [ "data/v1/package_v1.map", "data/v1/flag_v1.map", "data/v1/flag_v1.val", "data/v1/flag_v1.info", "data/v2/package_v2.map", "data/v2/flag_v2.map", "data/v2/flag_v2.val", "data/v2/flag_v2.info", ], test_suites: [ "general-tests", ], jarjar_rules: "jarjar.txt", } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/jarjar.txt ================================================ rule android.aconfig.storage.AconfigStorageException android.aconfig.storage.test.AconfigStorageException rule android.aconfig.storage.FlagTable android.aconfig.storage.test.FlagTable rule android.aconfig.storage.PackageTable android.aconfig.storage.test.PackageTable rule android.aconfig.storage.ByteBufferReader android.aconfig.storage.test.ByteBufferReader rule android.aconfig.storage.FlagType android.aconfig.storage.test.FlagType rule android.aconfig.storage.SipHasher13 android.aconfig.storage.test.SipHasher13 rule android.aconfig.storage.FileType android.aconfig.storage.test.FileType rule android.aconfig.storage.FlagValueList android.aconfig.storage.test.FlagValueList rule android.aconfig.storage.TableUtils android.aconfig.storage.test.TableUtils rule android.aconfig.storage.AconfigPackageImpl android.aconfig.storage.test.AconfigPackageImpl rule android.aconfig.storage.StorageFileProvider android.aconfig.storage.test.StorageFileProvider rule android.aconfig.storage.FlagTable$* android.aconfig.storage.test.FlagTable$@1 rule android.aconfig.storage.PackageTable$* android.aconfig.storage.test.PackageTable$@1 rule android.aconfig.storage.FlagValueList$* android.aconfig.storage.test.FlagValueList@1 rule android.aconfig.storage.SipHasher13$* android.aconfig.storage.test.SipHasher13@1 ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import android.aconfig.storage.ByteBufferReader; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; @RunWith(JUnit4.class) public class ByteBufferReaderTest { @Test public void testReadByte() { ByteBuffer buffer = ByteBuffer.allocate(1); byte expect = 10; buffer.put(expect).rewind(); ByteBufferReader reader = new ByteBufferReader(buffer); assertEquals(expect, reader.readByte()); } @Test public void testReadShort() { ByteBuffer buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.LITTLE_ENDIAN); short expect = Short.MAX_VALUE; buffer.putShort(expect).rewind(); ByteBufferReader reader = new ByteBufferReader(buffer); assertEquals(expect, reader.readShort()); } @Test public void testReadInt() { ByteBuffer buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.LITTLE_ENDIAN); int expect = 10000; buffer.putInt(expect).rewind(); ByteBufferReader reader = new ByteBufferReader(buffer); assertEquals(expect, reader.readInt()); } @Test public void testReadString() { String expect = "test read string"; byte[] bytes = expect.getBytes(StandardCharsets.UTF_8); ByteBuffer buffer = ByteBuffer.allocate(expect.length() * 2 + 4); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(expect.length()).put(bytes).rewind(); ByteBufferReader reader = new ByteBufferReader(buffer); assertEquals(expect, reader.readString()); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import android.aconfig.storage.FileType; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagType; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Objects; import java.util.concurrent.CyclicBarrier; @RunWith(JUnit4.class) public class FlagTableTest { @Test public void testFlagTable_rightHeader() throws Exception { FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagTable.Header header = flagTable.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.FLAG_MAP, header.getFileType()); assertEquals(321, header.getFileSize()); assertEquals(8, header.getNumFlags()); assertEquals(31, header.getBucketOffset()); assertEquals(99, header.getNodeOffset()); } @Test public void testFlagTable_rightNode() throws Exception { FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagTable.Node node1 = flagTable.get(0, "enabled_ro"); FlagTable.Node node2 = flagTable.get(0, "enabled_rw"); FlagTable.Node node3 = flagTable.get(2, "enabled_rw"); FlagTable.Node node4 = flagTable.get(1, "disabled_rw"); FlagTable.Node node5 = flagTable.get(1, "enabled_fixed_ro"); FlagTable.Node node6 = flagTable.get(1, "enabled_ro"); FlagTable.Node node7 = flagTable.get(2, "enabled_fixed_ro"); FlagTable.Node node8 = flagTable.get(0, "disabled_rw"); assertEquals("enabled_ro", node1.getFlagName()); assertEquals("enabled_rw", node2.getFlagName()); assertEquals("enabled_rw", node3.getFlagName()); assertEquals("disabled_rw", node4.getFlagName()); assertEquals("enabled_fixed_ro", node5.getFlagName()); assertEquals("enabled_ro", node6.getFlagName()); assertEquals("enabled_fixed_ro", node7.getFlagName()); assertEquals("disabled_rw", node8.getFlagName()); assertEquals(0, node1.getPackageId()); assertEquals(0, node2.getPackageId()); assertEquals(2, node3.getPackageId()); assertEquals(1, node4.getPackageId()); assertEquals(1, node5.getPackageId()); assertEquals(1, node6.getPackageId()); assertEquals(2, node7.getPackageId()); assertEquals(0, node8.getPackageId()); assertEquals(FlagType.ReadOnlyBoolean, node1.getFlagType()); assertEquals(FlagType.ReadWriteBoolean, node2.getFlagType()); assertEquals(FlagType.ReadWriteBoolean, node3.getFlagType()); assertEquals(FlagType.ReadWriteBoolean, node4.getFlagType()); assertEquals(FlagType.FixedReadOnlyBoolean, node5.getFlagType()); assertEquals(FlagType.ReadOnlyBoolean, node6.getFlagType()); assertEquals(FlagType.FixedReadOnlyBoolean, node7.getFlagType()); assertEquals(FlagType.ReadWriteBoolean, node8.getFlagType()); assertEquals(1, node1.getFlagIndex()); assertEquals(2, node2.getFlagIndex()); assertEquals(1, node3.getFlagIndex()); assertEquals(0, node4.getFlagIndex()); assertEquals(1, node5.getFlagIndex()); assertEquals(2, node6.getFlagIndex()); assertEquals(0, node7.getFlagIndex()); assertEquals(0, node8.getFlagIndex()); assertEquals(-1, node1.getNextOffset()); assertEquals(151, node2.getNextOffset()); assertEquals(-1, node3.getNextOffset()); assertEquals(-1, node4.getNextOffset()); assertEquals(236, node5.getNextOffset()); assertEquals(-1, node6.getNextOffset()); assertEquals(-1, node7.getNextOffset()); assertEquals(-1, node8.getNextOffset()); } @Test public void testFlagTable_multithreadsRead() throws Exception { FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(2)); int numberOfThreads = 8; Thread[] threads = new Thread[numberOfThreads]; final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1); String[] expects = { "enabled_ro", "enabled_rw", "enabled_rw", "disabled_rw", "enabled_fixed_ro", "enabled_ro", "enabled_fixed_ro", "disabled_rw" }; int[] packageIds = {0, 0, 2, 1, 1, 1, 2, 0}; for (int i = 0; i < numberOfThreads; i++) { String expectRet = expects[i]; int packageId = packageIds[i]; threads[i] = new Thread() { @Override public void run() { try { gate.await(); } catch (Exception e) { } for (int j = 0; j < 10; j++) { if (!Objects.equals( expectRet, flagTable.get(packageId, expectRet).getFlagName())) { throw new RuntimeException(); } } } }; threads[i].start(); } gate.await(); for (int i = 0; i < numberOfThreads; i++) { threads[i].join(); } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.aconfig.storage.FileType; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Objects; import java.util.concurrent.CyclicBarrier; @RunWith(JUnit4.class) public class FlagValueListTest { @Test public void testFlagValueList_rightHeader() throws Exception { FlagValueList flagValueList = FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); FlagValueList.Header header = flagValueList.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.FLAG_VAL, header.getFileType()); assertEquals(35, header.getFileSize()); assertEquals(8, header.getNumFlags()); assertEquals(27, header.getBooleanValueOffset()); } @Test public void testFlagValueList_rightNode() throws Exception { FlagValueList flagValueList = FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true}; assertEquals(expected.length, flagValueList.size()); for (int i = 0; i < flagValueList.size(); i++) { assertEquals(expected[i], flagValueList.getBoolean(i)); } } @Test public void testFlagValueList_getValue() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagValueList flagValueList = FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1"); FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw"); assertTrue(flagValueList.getBoolean(pNode.getBooleanStartIndex() + fNode.getFlagIndex())); pNode = packageTable.get("com.android.aconfig.storage.test_4"); fNode = flagTable.get(pNode.getPackageId(), "enabled_fixed_ro"); assertTrue(flagValueList.getBoolean(pNode.getBooleanStartIndex() + fNode.getFlagIndex())); } @Test public void testFlagValueList_multithreadsRead() throws Exception { FlagValueList flagValueList = FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(2)); int numberOfThreads = 8; Thread[] threads = new Thread[numberOfThreads]; final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1); boolean[] expects = {false, true, true, false, true, true, true, true}; for (int i = 0; i < numberOfThreads; i++) { boolean expectRet = expects[i]; int position = i; threads[i] = new Thread() { @Override public void run() { try { gate.await(); } catch (Exception e) { } for (int j = 0; j < 10; j++) { if (!Objects.equals( expectRet, flagValueList.getBoolean(position))) { throw new RuntimeException(); } } } }; threads[i].start(); } gate.await(); for (int i = 0; i < numberOfThreads; i++) { threads[i].join(); } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.aconfig.storage.FileType; import android.aconfig.storage.PackageTable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.concurrent.CyclicBarrier; @RunWith(JUnit4.class) public class PackageTableTest { @Test public void testPackageTable_rightHeader() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); PackageTable.Header header = packageTable.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.PACKAGE_MAP, header.getFileType()); assertEquals(209, header.getFileSize()); assertEquals(3, header.getNumPackages()); assertEquals(31, header.getBucketOffset()); assertEquals(59, header.getNodeOffset()); } @Test public void testPackageTable_rightHeader_v2() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); PackageTable.Header header = packageTable.getHeader(); assertEquals(2, header.getVersion()); assertEquals("mockup", header.getContainer()); assertEquals(FileType.PACKAGE_MAP, header.getFileType()); assertEquals(233, header.getFileSize()); assertEquals(3, header.getNumPackages()); assertEquals(31, header.getBucketOffset()); assertEquals(59, header.getNodeOffset()); } @Test public void testPackageTable_rightNode() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1"); PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2"); PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4"); assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName()); assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName()); assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName()); assertEquals(0, node1.getPackageId()); assertEquals(1, node2.getPackageId()); assertEquals(2, node4.getPackageId()); assertEquals(0, node1.getBooleanStartIndex()); assertEquals(3, node2.getBooleanStartIndex()); assertEquals(6, node4.getBooleanStartIndex()); assertEquals(159, node1.getNextOffset()); assertEquals(-1, node2.getNextOffset()); assertEquals(-1, node4.getNextOffset()); assertFalse(node1.hasPackageFingerprint()); assertFalse(node2.hasPackageFingerprint()); assertFalse(node4.hasPackageFingerprint()); } @Test public void testPackageTable_rightNode_v2() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1"); PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2"); PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4"); assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName()); assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName()); assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName()); assertEquals(0, node1.getPackageId()); assertEquals(1, node2.getPackageId()); assertEquals(2, node4.getPackageId()); assertEquals(0, node1.getBooleanStartIndex()); assertEquals(3, node2.getBooleanStartIndex()); assertEquals(6, node4.getBooleanStartIndex()); assertEquals(175, node1.getNextOffset()); assertEquals(-1, node2.getNextOffset()); assertEquals(-1, node4.getNextOffset()); assertTrue(node1.hasPackageFingerprint()); assertTrue(node2.hasPackageFingerprint()); assertTrue(node4.hasPackageFingerprint()); assertEquals(-3197795563119393530L, node1.getPackageFingerprint()); assertEquals(4431940502274857964L, node2.getPackageFingerprint()); assertEquals(-2213514155997929241L, node4.getPackageFingerprint()); } @Test public void testPackageTable_getPackageList() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); Set packages = new HashSet<>(packageTable.getPackageList()); assertEquals(3, packages.size()); assertTrue(packages.contains("com.android.aconfig.storage.test_1")); assertTrue(packages.contains("com.android.aconfig.storage.test_2")); assertTrue(packages.contains("com.android.aconfig.storage.test_4")); packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); packages = new HashSet<>(packageTable.getPackageList()); assertEquals(3, packages.size()); assertTrue(packages.contains("com.android.aconfig.storage.test_1")); assertTrue(packages.contains("com.android.aconfig.storage.test_2")); assertTrue(packages.contains("com.android.aconfig.storage.test_4")); } @Test public void testPackageTable_multithreadsRead() throws Exception { PackageTable packageTable = PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); int numberOfThreads = 3; Thread[] threads = new Thread[numberOfThreads]; final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1); String[] expects = { "com.android.aconfig.storage.test_1", "com.android.aconfig.storage.test_2", "com.android.aconfig.storage.test_4" }; for (int i = 0; i < numberOfThreads; i++) { final String packageName = expects[i]; threads[i] = new Thread() { @Override public void run() { try { gate.await(); } catch (Exception e) { } for (int j = 0; j < 10; j++) { if (!Objects.equals( packageName, packageTable.get(packageName).getPackageName())) { throw new RuntimeException(); } } } }; threads[i].start(); } gate.await(); for (int i = 0; i < numberOfThreads; i++) { threads[i].join(); } } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/SipHasher13Test.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static java.nio.charset.StandardCharsets.UTF_8; import android.aconfig.storage.SipHasher13; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class SipHasher13Test { @Test public void testSipHash_hashString() throws Exception { String testStr = "com.google.android.test"; long result = SipHasher13.hash(testStr.getBytes(UTF_8)); assertEquals(0xF86572EFF9C4A0C1L, result); testStr = "abcdefg"; result = SipHasher13.hash(testStr.getBytes(UTF_8)); assertEquals(0x2295EF44BD078AE9L, result); testStr = "abcdefgh"; result = SipHasher13.hash(testStr.getBytes(UTF_8)); assertEquals(0x5CD7657FA7F96C16L, result); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/StorageFileProviderTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import android.aconfig.storage.StorageFileProvider; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.nio.file.Paths; import java.util.List; @RunWith(JUnit4.class) public class StorageFileProviderTest { @Test public void testlistContainers() throws Exception { StorageFileProvider p = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); String[] excludes = {}; List containers = p.listContainers(excludes); assertEquals(2, containers.size()); excludes = new String[] {"mock.v1"}; containers = p.listContainers(excludes); assertEquals(1, containers.size()); p = new StorageFileProvider("fake/path/", "fake/path/"); containers = p.listContainers(excludes); assertTrue(containers.isEmpty()); } @Test public void testLoadFiles() throws Exception { StorageFileProvider p = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); PackageTable pt = p.getPackageTable("mock.v1"); assertNotNull(pt); FlagTable f = p.getFlagTable("mock.v1"); assertNotNull(f); FlagValueList v = p.getFlagValueList("mock.v1"); assertNotNull(v); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import java.io.FileInputStream; import java.io.InputStream; import java.nio.ByteBuffer; public final class TestDataUtils { private static final String TEST_PACKAGE_MAP_PATH = "mock.v%d.package.map"; private static final String TEST_FLAG_MAP_PATH = "mock.v%d.flag.map"; private static final String TEST_FLAG_VAL_PATH = "mock.v%d.val"; private static final String TEST_FLAG_INFO_PATH = "mock.v%d.info"; public static final String TESTDATA_PATH = "/data/local/tmp/aconfig_storage_file_test_java/testdata/"; public static ByteBuffer getTestPackageMapByteBuffer(int version) throws Exception { return readFile(TESTDATA_PATH + String.format(TEST_PACKAGE_MAP_PATH, version)); } public static ByteBuffer getTestFlagMapByteBuffer(int version) throws Exception { return readFile(TESTDATA_PATH + String.format(TEST_FLAG_MAP_PATH, version)); } public static ByteBuffer getTestFlagValByteBuffer(int version) throws Exception { return readFile(TESTDATA_PATH + String.format(TEST_FLAG_VAL_PATH, version)); } public static ByteBuffer getTestFlagInfoByteBuffer(int version) throws Exception { return readFile(TESTDATA_PATH + String.format(TEST_FLAG_INFO_PATH, version)); } private static ByteBuffer readFile(String fileName) throws Exception { InputStream input = new FileInputStream(fileName); return ByteBuffer.wrap(input.readAllBytes()); } } ================================================ FILE: tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "aconfig_storage/aconfig_storage_file.hpp" using namespace android::base; using namespace aconfig_storage; void verify_value(const FlagValueSummary& flag, const std::string& package_name, const std::string& flag_name, const std::string& flag_val, const std::string& value_type) { ASSERT_EQ(flag.package_name, package_name); ASSERT_EQ(flag.flag_name, flag_name); ASSERT_EQ(flag.flag_value, flag_val); ASSERT_EQ(flag.value_type, value_type); } void verify_value_info(const FlagValueAndInfoSummary& flag, const std::string& package_name, const std::string& flag_name, const std::string& flag_val, const std::string& value_type, bool is_readwrite, bool has_server_override, bool has_local_override) { ASSERT_EQ(flag.package_name, package_name); ASSERT_EQ(flag.flag_name, flag_name); ASSERT_EQ(flag.flag_value, flag_val); ASSERT_EQ(flag.value_type, value_type); ASSERT_EQ(flag.is_readwrite, is_readwrite); ASSERT_EQ(flag.has_server_override, has_server_override); ASSERT_EQ(flag.has_local_override, has_local_override); } Result> get_flag_list_result( const std::string version) { auto const test_base_dir = GetExecutableDirectory(); auto const test_dir = test_base_dir + "/data/v" + version; auto const package_map = test_dir + "/package_v" + version + ".map"; auto const flag_map = test_dir + "/flag_v" + version + ".map"; auto const flag_val = test_dir + "/flag_v" + version + ".val"; return aconfig_storage::list_flags(package_map, flag_map, flag_val); } Result> get_flag_list_result_with_info( const std::string version) { auto const test_base_dir = GetExecutableDirectory(); auto const test_dir = test_base_dir + "/data/v" + version; auto const package_map = test_dir + "/package_v" + version + ".map"; auto const flag_map = test_dir + "/flag_v" + version + ".map"; auto const flag_val = test_dir + "/flag_v" + version + ".val"; auto const flag_info = test_dir + "/flag_v" + version + ".info"; return aconfig_storage::list_flags_with_info(package_map, flag_map, flag_val, flag_info); } TEST(AconfigStorageFileTest, test_list_flag) { auto flag_list_result = get_flag_list_result("1"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); verify_value(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", "false", "ReadWriteBoolean"); verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", "true", "ReadOnlyBoolean"); verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", "true", "ReadWriteBoolean"); verify_value(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", "false", "ReadWriteBoolean"); verify_value(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", "true", "ReadOnlyBoolean"); verify_value(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", "true", "ReadWriteBoolean"); } // TODO: b/376256472 - Use parameterized tests. TEST(AconfigStorageFileTest, test_list_flag_v2) { auto flag_list_result = get_flag_list_result("2"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); verify_value(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", "false", "ReadWriteBoolean"); verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", "true", "ReadOnlyBoolean"); verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", "true", "ReadWriteBoolean"); verify_value(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", "false", "ReadWriteBoolean"); verify_value(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", "true", "ReadOnlyBoolean"); verify_value(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", "true", "ReadWriteBoolean"); } TEST(AconfigStorageFileTest, test_list_flag_with_info) { auto flag_list_result = get_flag_list_result_with_info("1"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", "false", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", "true", "ReadOnlyBoolean", false, false, false); verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", "true", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", "false", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, false, false); verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", "true", "ReadOnlyBoolean", false, false, false); verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, false, false); verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", "true", "ReadWriteBoolean", true, false, false); } TEST(AconfigStorageFileTest, test_list_flag_with_info_v2) { auto flag_list_result = get_flag_list_result_with_info("2"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", "false", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", "true", "ReadOnlyBoolean", false, false, false); verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", "true", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", "false", "ReadWriteBoolean", true, false, false); verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, false, false); verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", "true", "ReadOnlyBoolean", false, false, false); verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, false, false); verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", "true", "ReadWriteBoolean", true, false, false); } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "aconfig_storage_read_api.defaults", edition: "2021", lints: "none", srcs: ["src/lib.rs"], rustlibs: [ "libanyhow", "libmemmap2", "libcxx", "libthiserror", "libaconfig_storage_file", ], } rust_library { name: "libaconfig_storage_read_api", crate_name: "aconfig_storage_read_api", host_supported: true, defaults: ["aconfig_storage_read_api.defaults"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", } rust_test_host { name: "aconfig_storage_read_api.test", test_suites: ["general-tests"], defaults: ["aconfig_storage_read_api.defaults"], rustlibs: [ "librand", ], data: [ "tests/data/v1/package_v1.map", "tests/data/v1/flag_v1.map", "tests/data/v1/flag_v1.val", "tests/data/v1/flag_v1.info", ], } // cxx source codegen from rust api genrule { name: "libcxx_aconfig_storage_read_api_bridge_code", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.cc"], } // cxx header codegen from rust api genrule { name: "libcxx_aconfig_storage_read_api_bridge_header", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) --header > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.h"], } // a static cc lib based on generated code rust_ffi_static { name: "libaconfig_storage_read_api_cxx_bridge", crate_name: "aconfig_storage_read_api_cxx_bridge", host_supported: true, vendor_available: true, product_available: true, defaults: ["aconfig_storage_read_api.defaults"], apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", } // flag read api cc interface cc_library { name: "libaconfig_storage_read_api_cc", srcs: ["aconfig_storage_read_api.cpp"], generated_headers: [ "cxx-bridge-header", "libcxx_aconfig_storage_read_api_bridge_header", ], generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"], whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"], export_include_dirs: ["include"], static_libs: [ "libbase", ], host_supported: true, vendor_available: true, product_available: true, apex_available: [ "//apex_available:platform", "//apex_available:anyapex", ], min_sdk_version: "29", target: { linux: { version_script: "libaconfig_storage_read_api_cc.map", }, }, double_loadable: true, afdo: true, } cc_defaults { name: "aconfig_lib_cc_shared_link.defaults", shared_libs: select(release_flag("RELEASE_READ_FROM_NEW_STORAGE"), { true: ["libaconfig_storage_read_api_cc"], default: [], }), } cc_defaults { name: "aconfig_lib_cc_static_link.defaults", shared_libs: [ "libaconfig_storage_read_api_cc", "liblog", ], } rust_ffi_shared { name: "libaconfig_storage_read_api_rust_jni", crate_name: "aconfig_storage_read_api_rust_jni", srcs: ["srcs/lib.rs"], rustlibs: [ "libaconfig_storage_file", "libaconfig_storage_read_api", "libanyhow", "libjni", ], prefer_rlib: true, } java_library { name: "libaconfig_storage_read_api_java", srcs: [ "srcs/android/aconfig/storage/AconfigStorageReadAPI.java", "srcs/android/aconfig/storage/FlagReadContext.java", "srcs/android/aconfig/storage/PackageReadContext.java", ], required: ["libaconfig_storage_read_api_rust_jni"], min_sdk_version: "UpsideDownCake", apex_available: [ "//apex_available:anyapex", "//apex_available:platform", ], } java_library { name: "aconfig_storage_reader_java", srcs: [ "srcs/android/os/flagging/*.java", ], libs: [ "unsupportedappusage", ], static_libs: [ "aconfig_storage_file_java", ], sdk_version: "current", visibility: [ "//frameworks/base", "//build/make/tools/aconfig/aconfig_storage_read_api/tests", ], } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/Cargo.toml ================================================ [package] name = "aconfig_storage_read_api" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] rand = "0.8.5" anyhow = "1.0.69" memmap2 = "0.8.0" cxx = "1.0" thiserror = "1.0.56" aconfig_storage_file = { path = "../aconfig_storage_file" } [build-dependencies] protobuf-codegen = "3.2.0" cxx-build = "1.0" ================================================ FILE: tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp ================================================ #include #include #include #include #include #include #include "rust/cxx.h" #include "aconfig_storage/lib.rs.h" #include "aconfig_storage/aconfig_storage_read_api.hpp" namespace aconfig_storage { /// Storage location pb file static constexpr char kStorageDir[] = "/metadata/aconfig"; /// destructor MappedStorageFile::~MappedStorageFile() { munmap(file_ptr, file_size); } /// Get storage file path static Result find_storage_file( std::string const& storage_dir, std::string const& container, StorageFileType file_type) { switch(file_type) { case StorageFileType::package_map: return storage_dir + "/maps/" + container + ".package.map"; case StorageFileType::flag_map: return storage_dir + "/maps/" + container + ".flag.map"; case StorageFileType::flag_val: return storage_dir + "/boot/" + container + ".val"; case StorageFileType::flag_info: return storage_dir + "/boot/" + container + ".info"; default: auto result = Result(); result.errmsg = "Invalid storage file type"; return result; } } namespace private_internal_api { /// Get mapped file implementation. Result get_mapped_file_impl( std::string const& storage_dir, std::string const& container, StorageFileType file_type) { auto file_result = find_storage_file(storage_dir, container, file_type); if (!file_result.ok()) { auto result = Result(); result.errmsg = file_result.error(); return result; } return map_storage_file(*file_result); } } // namespace private internal api /// Map a storage file Result map_storage_file(std::string const& file) { android::base::unique_fd ufd(open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY)); if (ufd.get() == -1) { auto result = Result(); result.errmsg = std::string("failed to open ") + file + ": " + strerror(errno); return result; }; struct stat fd_stat; if (fstat(ufd.get(), &fd_stat) < 0) { auto result = Result(); result.errmsg = std::string("fstat failed: ") + strerror(errno); return result; } size_t file_size = fd_stat.st_size; void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, ufd.get(), 0); if (map_result == MAP_FAILED) { auto result = Result(); result.errmsg = std::string("mmap failed: ") + strerror(errno); return result; } auto mapped_file = new MappedStorageFile(); mapped_file->file_ptr = map_result; mapped_file->file_size = file_size; return mapped_file; } /// Map from StoredFlagType to FlagValueType Result map_to_flag_value_type( StoredFlagType stored_type) { switch (stored_type) { case StoredFlagType::ReadWriteBoolean: case StoredFlagType::ReadOnlyBoolean: case StoredFlagType::FixedReadOnlyBoolean: return FlagValueType::Boolean; default: auto result = Result(); result.errmsg = "Unsupported stored flag type"; return result; } } /// Get mapped storage file Result get_mapped_file( std::string const& container, StorageFileType file_type) { return private_internal_api::get_mapped_file_impl( kStorageDir, container, file_type); } /// Get storage file version number Result get_storage_file_version( std::string const& file_path) { auto version_cxx = get_storage_file_version_cxx( rust::Str(file_path.c_str())); if (version_cxx.query_success) { return version_cxx.version_number; } else { auto result = Result(); result.errmsg = version_cxx.error_message.c_str(); return result; } } /// Get package context Result get_package_read_context( MappedStorageFile const& file, std::string const& package) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto context_cxx = get_package_read_context_cxx(content, rust::Str(package.c_str())); if (context_cxx.query_success) { auto context = PackageReadContext(); context.package_exists = context_cxx.package_exists; context.package_id = context_cxx.package_id; context.boolean_start_index = context_cxx.boolean_start_index; return context; } else { auto result = Result(); result.errmsg = context_cxx.error_message.c_str(); return result; } } /// Get flag read context Result get_flag_read_context( MappedStorageFile const& file, uint32_t package_id, std::string const& flag_name){ auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto context_cxx = get_flag_read_context_cxx(content, package_id, rust::Str(flag_name.c_str())); if (context_cxx.query_success) { auto context = FlagReadContext(); context.flag_exists = context_cxx.flag_exists; context.flag_type = static_cast(context_cxx.flag_type); context.flag_index = context_cxx.flag_index; return context; } else { auto result = Result(); result.errmsg = context_cxx.error_message.c_str(); return result; } } /// Get boolean flag value Result get_boolean_flag_value( MappedStorageFile const& file, uint32_t index) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto value_cxx = get_boolean_flag_value_cxx(content, index); if (value_cxx.query_success) { return value_cxx.flag_value; } else { auto result = Result(); result.errmsg = value_cxx.error_message.c_str(); return result; } } /// Get boolean flag attribute Result get_flag_attribute( MappedStorageFile const& file, FlagValueType value_type, uint32_t index) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto info_cxx = get_flag_attribute_cxx( content, static_cast(value_type), index); if (info_cxx.query_success) { return info_cxx.flag_attribute; } else { auto result = Result(); result.errmsg = info_cxx.error_message.c_str(); return result; } } } // namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_read_api/build.rs ================================================ fn main() { let _ = cxx_build::bridge("src/lib.rs"); println!("cargo:rerun-if-changed=src/lib.rs"); } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp ================================================ #pragma once #include #include #include namespace aconfig_storage { /// Storage file type enum, to be consistent with the one defined in /// aconfig_storage_file/src/lib.rs enum StorageFileType { package_map, flag_map, flag_val, flag_info }; /// Flag type enum, to be consistent with the one defined in /// aconfig_storage_file/src/lib.rs enum StoredFlagType { ReadWriteBoolean = 0, ReadOnlyBoolean = 1, FixedReadOnlyBoolean = 2, }; /// Flag value type enum, to be consistent with the one defined in /// aconfig_storage_file/src/lib.rs enum FlagValueType { Boolean = 0, }; /// Flag info enum, to be consistent with the one defined in /// aconfig_storage_file/src/flag_info.rs enum FlagInfoBit { HasServerOverride = 1<<0, IsReadWrite = 1<<1, HasLocalOverride = 1<<2, }; /// Mapped storage file struct MappedStorageFile { void* file_ptr; size_t file_size; virtual ~MappedStorageFile(); }; /// Package read context query result struct PackageReadContext { bool package_exists; uint32_t package_id; uint32_t boolean_start_index; }; /// Flag read context query result struct FlagReadContext { bool flag_exists; StoredFlagType flag_type; uint16_t flag_index; }; template class Result { public: Result() : data() , errmsg() , has_data(false) {} Result(T const& value) : data(value) , errmsg() , has_data(true) {} bool ok() { return has_data; } T& operator*() { assert(has_data); return data; } T* operator->() { assert(has_data); return &data; } std::string const& error() { assert(!has_data); return errmsg; } T data; std::string errmsg; bool has_data; }; /// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY namespace private_internal_api { Result get_mapped_file_impl( std::string const& pb_file, std::string const& container, StorageFileType file_type); } // namespace private_internal_api /// Map a storage file Result map_storage_file( std::string const& file); /// Map from StoredFlagType to FlagValueType /// \input stored_type: stored flag type in the storage file /// \returns the flag value type enum Result map_to_flag_value_type( StoredFlagType stored_type); /// Get mapped storage file /// \input container: stoarge container name /// \input file_type: storage file type enum /// \returns a MappedStorageFileQuery Result get_mapped_file( std::string const& container, StorageFileType file_type); /// Get storage file version number /// \input file_path: the path to the storage file /// \returns the storage file version Result get_storage_file_version( std::string const& file_path); /// Get package read context /// \input file: mapped storage file /// \input package: the flag package name /// \returns a package read context Result get_package_read_context( MappedStorageFile const& file, std::string const& package); /// Get flag read context /// \input file: mapped storage file /// \input package_id: the flag package id obtained from package offset query /// \input flag_name: flag name /// \returns the flag read context Result get_flag_read_context( MappedStorageFile const& file, uint32_t package_id, std::string const& flag_name); /// Get boolean flag value /// \input file: mapped storage file /// \input index: the boolean flag index in the file /// \returns the boolean flag value Result get_boolean_flag_value( MappedStorageFile const& file, uint32_t index); /// Get boolean flag attribute /// \input file: mapped storage file /// \input value_type: flag value type /// \input index: the boolean flag index in the file /// \returns the boolean flag attribute Result get_flag_attribute( MappedStorageFile const& file, FlagValueType value_type, uint32_t index); } // namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag value query module defines the flag value file read from mapped bytes use crate::AconfigStorageError; use aconfig_storage_file::{ flag_info::FlagInfoHeader, read_u8_from_bytes, FlagValueType, MAX_SUPPORTED_FILE_VERSION, }; use anyhow::anyhow; /// Get flag attribute bitfield pub fn find_flag_attribute( buf: &[u8], flag_type: FlagValueType, flag_index: u32, ) -> Result { let interpreted_header = FlagInfoHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot read storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } // get byte offset to the flag info let mut head = match flag_type { FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize, }; if head >= interpreted_header.file_size as usize { return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!( "Flag info offset goes beyond the end of the file." ))); } let val = read_u8_from_bytes(buf, &mut head)?; Ok(val) } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{ test_utils::create_test_flag_info_list, FlagInfoBit, DEFAULT_FILE_VERSION, }; #[test] // this test point locks down query if flag has server override fn test_is_flag_sticky() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); for offset in 0..8 { let attribute = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap(); assert_eq!((attribute & FlagInfoBit::HasServerOverride as u8) != 0u8, false); } } #[test] // this test point locks down query if flag is readwrite fn test_is_flag_readwrite() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); let baseline: Vec = vec![true, false, true, true, false, false, false, true]; for offset in 0..8 { let attribute = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap(); assert_eq!( (attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, baseline[offset as usize] ); } } #[test] // this test point locks down query if flag has local override fn test_flag_has_override() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); for offset in 0..8 { let attribute = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap(); assert_eq!((attribute & FlagInfoBit::HasLocalOverride as u8) != 0u8, false); } } #[test] // this test point locks down query beyond the end of boolean section fn test_boolean_out_of_range() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); let error = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, 8).unwrap_err(); assert_eq!( format!("{:?}", error), "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)" ); } #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { let mut info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); info_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_info = info_list.into_bytes(); let error = find_flag_attribute(&flag_info[..], FlagValueType::Boolean, 4).unwrap_err(); assert_eq!( format!("{:?}", error), format!( "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})", MAX_SUPPORTED_FILE_VERSION + 1, MAX_SUPPORTED_FILE_VERSION ) ); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag table query module defines the flag table file read from mapped bytes use crate::AconfigStorageError; use aconfig_storage_file::{ flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes, StoredFlagType, MAX_SUPPORTED_FILE_VERSION, }; use anyhow::anyhow; /// Flag table query return #[derive(PartialEq, Debug)] pub struct FlagReadContext { pub flag_type: StoredFlagType, pub flag_index: u16, } /// Query flag read context: flag type and within package flag index pub fn find_flag_read_context( buf: &[u8], package_id: u32, flag: &str, ) -> Result, AconfigStorageError> { let interpreted_header = FlagTableHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot read storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4; let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets); let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize; let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize; if flag_node_offset < interpreted_header.node_offset as usize || flag_node_offset >= interpreted_header.file_size as usize { return Ok(None); } loop { let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?; if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag { return Ok(Some(FlagReadContext { flag_type: interpreted_node.flag_type, flag_index: interpreted_node.flag_index, })); } match interpreted_node.next_offset { Some(offset) => flag_node_offset = offset as usize, None => return Ok(None), } } } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{test_utils::create_test_flag_table, DEFAULT_FILE_VERSION}; #[test] // this test point locks down table query fn test_flag_query() { let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes(); let baseline = vec![ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16), (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16), (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16), (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16), (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16), (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16), (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), ]; for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() { let flag_context = find_flag_read_context(&flag_table[..], package_id, flag_name).unwrap().unwrap(); assert_eq!(flag_context.flag_type, flag_type); assert_eq!(flag_context.flag_index, flag_index); } } #[test] // this test point locks down table query of a non exist flag fn test_not_existed_flag_query() { let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes(); let flag_context = find_flag_read_context(&flag_table[..], 1, "disabled_fixed_ro").unwrap(); assert_eq!(flag_context, None); let flag_context = find_flag_read_context(&flag_table[..], 2, "disabled_rw").unwrap(); assert_eq!(flag_context, None); } #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { let mut table = create_test_flag_table(DEFAULT_FILE_VERSION); table.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_table = table.into_bytes(); let error = find_flag_read_context(&flag_table[..], 0, "enabled_ro").unwrap_err(); assert_eq!( format!("{:?}", error), format!( "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})", MAX_SUPPORTED_FILE_VERSION + 1, MAX_SUPPORTED_FILE_VERSION ) ); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag value query module defines the flag value file read from mapped bytes use crate::AconfigStorageError; use aconfig_storage_file::{ flag_value::FlagValueHeader, read_u8_from_bytes, MAX_SUPPORTED_FILE_VERSION, }; use anyhow::anyhow; /// Query flag value pub fn find_boolean_flag_value(buf: &[u8], flag_index: u32) -> Result { let interpreted_header = FlagValueHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot read storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } // Find byte offset to the flag value, each boolean flag cost one byte to store let mut head = (interpreted_header.boolean_value_offset + flag_index) as usize; if head >= interpreted_header.file_size as usize { return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!( "Flag value offset goes beyond the end of the file." ))); } let val = read_u8_from_bytes(buf, &mut head)?; Ok(val == 1) } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION}; #[test] // this test point locks down flag value query fn test_flag_value_query() { let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let baseline: Vec = vec![false, true, true, false, true, true, true, true]; for (offset, expected_value) in baseline.into_iter().enumerate() { let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap(); assert_eq!(flag_value, expected_value); } } #[test] // this test point locks down query beyond the end of boolean section fn test_boolean_out_of_range() { let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err(); assert_eq!( format!("{:?}", error), "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)" ); } #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_value = value_list.into_bytes(); let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err(); assert_eq!( format!("{:?}", error), format!( "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})", MAX_SUPPORTED_FILE_VERSION + 1, MAX_SUPPORTED_FILE_VERSION ) ); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/lib.rs ================================================ /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage //! files. It provides four apis to interface with storage files: //! //! 1, function to get package read context //! pub fn get_packager_read_context(container: &str, package: &str) //! -> `Result>>` //! //! 2, function to get flag read context //! pub fn get_flag_read_context(container: &str, package_id: u32, flag: &str) //! -> `Result>>` //! //! 3, function to get the actual flag value given the global index (combined package and //! flag index). //! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result` //! //! 4, function to get storage file version without mmapping the file. //! pub fn get_storage_file_version(file_path: &str) -> Result //! //! Note these are low level apis that are expected to be only used in auto generated flag //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis //! please refer to the g3doc go/android-flags pub mod flag_info_query; pub mod flag_table_query; pub mod flag_value_query; pub mod mapped_file; pub mod package_table_query; pub use aconfig_storage_file::{AconfigStorageError, FlagValueType, StorageFileType}; pub use flag_table_query::FlagReadContext; pub use mapped_file::map_file; pub use package_table_query::PackageReadContext; use aconfig_storage_file::read_u32_from_bytes; use flag_info_query::find_flag_attribute; use flag_table_query::find_flag_read_context; use flag_value_query::find_boolean_flag_value; use package_table_query::find_package_read_context; use anyhow::anyhow; pub use memmap2::Mmap; use std::fs::File; use std::io::Read; /// Storage file location pub const STORAGE_LOCATION: &str = "/metadata/aconfig"; /// Get read only mapped storage files. /// /// \input container: the flag package container /// \input file_type: stoarge file type enum /// \return a result of read only mapped file /// /// # Safety /// /// The memory mapped file may have undefined behavior if there are writes to this /// file after being mapped. Ensure no writes can happen to this file while this /// mapping stays alive. pub unsafe fn get_mapped_storage_file( container: &str, file_type: StorageFileType, ) -> Result { unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION, container, file_type) } } /// Get package read context for a specific package. /// /// \input file: mapped package file /// \input package: package name /// /// \return /// If a package is found, it returns Ok(Some(PackageReadContext)) /// If a package is not found, it returns Ok(None) /// If errors out, it returns an Err(errmsg) pub fn get_package_read_context( file: &Mmap, package: &str, ) -> Result, AconfigStorageError> { find_package_read_context(file, package) } /// Get flag read context for a specific flag. /// /// \input file: mapped flag file /// \input package_id: package id obtained from package mapping file /// \input flag: flag name /// /// \return /// If a flag is found, it returns Ok(Some(FlagReadContext)) /// If a flag is not found, it returns Ok(None) /// If errors out, it returns an Err(errmsg) pub fn get_flag_read_context( file: &Mmap, package_id: u32, flag: &str, ) -> Result, AconfigStorageError> { find_flag_read_context(file, package_id, flag) } /// Get the boolean flag value. /// /// \input file: a byte slice, can be either &Mmap or &MapMut /// \input index: boolean flag offset /// /// \return /// If the provide offset is valid, it returns the boolean flag value, otherwise it /// returns the error message. pub fn get_boolean_flag_value(file: &[u8], index: u32) -> Result { find_boolean_flag_value(file, index) } /// Get storage file version number /// /// This function would read the first four bytes of the file and interpret it as the /// version number of the file. There are unit tests in aconfig_storage_file crate to /// lock down that for all storage files, the first four bytes will be the version /// number of the storage file pub fn get_storage_file_version(file_path: &str) -> Result { let mut file = File::open(file_path).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)) })?; let mut buffer = [0; 4]; file.read(&mut buffer).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!( "Failed to read 4 bytes from file {}: {}", file_path, errmsg )) })?; let mut head = 0; read_u32_from_bytes(&buffer, &mut head) } /// Get the flag attribute. /// /// \input file: a byte slice, can be either &Mmap or &MapMut /// \input flag_type: flag value type /// \input flag_index: flag index /// /// \return /// If the provide offset is valid, it returns the flag attribute bitfiled, otherwise it /// returns the error message. pub fn get_flag_attribute( file: &[u8], flag_type: FlagValueType, flag_index: u32, ) -> Result { find_flag_attribute(file, flag_type, flag_index) } // *************************************** // // CC INTERLOP // *************************************** // // Exported rust data structure and methods, c++ code will be generated #[cxx::bridge] mod ffi { // Storage file version query return for cc interlop pub struct VersionNumberQueryCXX { pub query_success: bool, pub error_message: String, pub version_number: u32, } // Package table query return for cc interlop pub struct PackageReadContextQueryCXX { pub query_success: bool, pub error_message: String, pub package_exists: bool, pub package_id: u32, pub boolean_start_index: u32, } // Flag table query return for cc interlop pub struct FlagReadContextQueryCXX { pub query_success: bool, pub error_message: String, pub flag_exists: bool, pub flag_type: u16, pub flag_index: u16, } // Flag value query return for cc interlop pub struct BooleanFlagValueQueryCXX { pub query_success: bool, pub error_message: String, pub flag_value: bool, } // Flag info query return for cc interlop pub struct FlagAttributeQueryCXX { pub query_success: bool, pub error_message: String, pub flag_attribute: u8, } // Rust export to c++ extern "Rust" { pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX; pub fn get_package_read_context_cxx( file: &[u8], package: &str, ) -> PackageReadContextQueryCXX; pub fn get_flag_read_context_cxx( file: &[u8], package_id: u32, flag: &str, ) -> FlagReadContextQueryCXX; pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX; pub fn get_flag_attribute_cxx( file: &[u8], flag_type: u16, flag_index: u32, ) -> FlagAttributeQueryCXX; } } /// Implement the package offset interlop return type, create from actual package offset api return type impl ffi::PackageReadContextQueryCXX { pub(crate) fn new( offset_result: Result, AconfigStorageError>, ) -> Self { match offset_result { Ok(offset_opt) => match offset_opt { Some(offset) => Self { query_success: true, error_message: String::from(""), package_exists: true, package_id: offset.package_id, boolean_start_index: offset.boolean_start_index, }, None => Self { query_success: true, error_message: String::from(""), package_exists: false, package_id: 0, boolean_start_index: 0, }, }, Err(errmsg) => Self { query_success: false, error_message: format!("{:?}", errmsg), package_exists: false, package_id: 0, boolean_start_index: 0, }, } } } /// Implement the flag offset interlop return type, create from actual flag offset api return type impl ffi::FlagReadContextQueryCXX { pub(crate) fn new(offset_result: Result, AconfigStorageError>) -> Self { match offset_result { Ok(offset_opt) => match offset_opt { Some(offset) => Self { query_success: true, error_message: String::from(""), flag_exists: true, flag_type: offset.flag_type as u16, flag_index: offset.flag_index, }, None => Self { query_success: true, error_message: String::from(""), flag_exists: false, flag_type: 0u16, flag_index: 0u16, }, }, Err(errmsg) => Self { query_success: false, error_message: format!("{:?}", errmsg), flag_exists: false, flag_type: 0u16, flag_index: 0u16, }, } } } /// Implement the flag value interlop return type, create from actual flag value api return type impl ffi::BooleanFlagValueQueryCXX { pub(crate) fn new(value_result: Result) -> Self { match value_result { Ok(value) => { Self { query_success: true, error_message: String::from(""), flag_value: value } } Err(errmsg) => Self { query_success: false, error_message: format!("{:?}", errmsg), flag_value: false, }, } } } /// Implement the flag info interlop return type, create from actual flag info api return type impl ffi::FlagAttributeQueryCXX { pub(crate) fn new(info_result: Result) -> Self { match info_result { Ok(info) => { Self { query_success: true, error_message: String::from(""), flag_attribute: info } } Err(errmsg) => Self { query_success: false, error_message: format!("{:?}", errmsg), flag_attribute: 0u8, }, } } } /// Implement the storage version number interlop return type, create from actual version number /// api return type impl ffi::VersionNumberQueryCXX { pub(crate) fn new(version_result: Result) -> Self { match version_result { Ok(version) => Self { query_success: true, error_message: String::from(""), version_number: version, }, Err(errmsg) => Self { query_success: false, error_message: format!("{:?}", errmsg), version_number: 0, }, } } } /// Get package read context cc interlop pub fn get_package_read_context_cxx(file: &[u8], package: &str) -> ffi::PackageReadContextQueryCXX { ffi::PackageReadContextQueryCXX::new(find_package_read_context(file, package)) } /// Get flag read context cc interlop pub fn get_flag_read_context_cxx( file: &[u8], package_id: u32, flag: &str, ) -> ffi::FlagReadContextQueryCXX { ffi::FlagReadContextQueryCXX::new(find_flag_read_context(file, package_id, flag)) } /// Get boolean flag value cc interlop pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> ffi::BooleanFlagValueQueryCXX { ffi::BooleanFlagValueQueryCXX::new(find_boolean_flag_value(file, offset)) } /// Get flag attribute cc interlop pub fn get_flag_attribute_cxx( file: &[u8], flag_type: u16, flag_index: u32, ) -> ffi::FlagAttributeQueryCXX { match FlagValueType::try_from(flag_type) { Ok(value_type) => { ffi::FlagAttributeQueryCXX::new(find_flag_attribute(file, value_type, flag_index)) } Err(errmsg) => ffi::FlagAttributeQueryCXX::new(Err(errmsg)), } } /// Get storage version number cc interlop pub fn get_storage_file_version_cxx(file_path: &str) -> ffi::VersionNumberQueryCXX { ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path)) } #[cfg(test)] mod tests { use super::*; use crate::mapped_file::get_mapped_file; use aconfig_storage_file::{FlagInfoBit, StoredFlagType}; use rand::Rng; use std::fs; fn create_test_storage_files() -> String { let mut rng = rand::thread_rng(); let number: u32 = rng.gen(); let storage_dir = String::from("/tmp/") + &number.to_string(); if std::fs::metadata(&storage_dir).is_ok() { fs::remove_dir_all(&storage_dir).unwrap(); } let maps_dir = storage_dir.clone() + "/maps"; let boot_dir = storage_dir.clone() + "/boot"; fs::create_dir(&storage_dir).unwrap(); fs::create_dir(&maps_dir).unwrap(); fs::create_dir(&boot_dir).unwrap(); let package_map = storage_dir.clone() + "/maps/mockup.package.map"; let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap(); fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap(); fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap(); fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap(); return storage_dir; } #[test] // this test point locks down flag package read context query fn test_package_context_query() { let storage_dir = create_test_storage_files(); let package_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::PackageMap).unwrap() }; let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); } #[test] // this test point locks down flag read context query fn test_flag_context_query() { let storage_dir = create_test_storage_files(); let flag_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagMap).unwrap() }; let baseline = vec![ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16), (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16), (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16), (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16), (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16), (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16), (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), ]; for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() { let flag_context = get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap(); assert_eq!(flag_context.flag_type, flag_type); assert_eq!(flag_context.flag_index, flag_index); } } #[test] // this test point locks down flag value query fn test_flag_value_query() { let storage_dir = create_test_storage_files(); let flag_value_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagVal).unwrap() }; let baseline: Vec = vec![false, true, true, false, true, true, true, true]; for (offset, expected_value) in baseline.into_iter().enumerate() { let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap(); assert_eq!(flag_value, expected_value); } } #[test] // this test point locks donw flag info query fn test_flag_info_query() { let storage_dir = create_test_storage_files(); let flag_info_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagInfo).unwrap() }; let is_rw: Vec = vec![true, false, true, true, false, false, false, true]; for (offset, expected_value) in is_rw.into_iter().enumerate() { let attribute = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap(); assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value); assert!((attribute & FlagInfoBit::HasServerOverride as u8) == 0u8); assert!((attribute & FlagInfoBit::HasLocalOverride as u8) == 0u8); } } #[test] // this test point locks down flag storage file version number query api fn test_storage_version_query() { assert_eq!(get_storage_file_version("./tests/data/v1/package_v1.map").unwrap(), 1); assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.map").unwrap(), 1); assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.val").unwrap(), 1); assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.info").unwrap(), 1); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::anyhow; use memmap2::Mmap; use std::fs::File; use crate::AconfigStorageError::{self, FileReadFail, MapFileFail, StorageFileNotFound}; use crate::StorageFileType; /// Get the read only memory mapping of a storage file /// /// # Safety /// /// The memory mapped file may have undefined behavior if there are writes to this /// file after being mapped. Ensure no writes can happen to this file while this /// mapping stays alive. pub unsafe fn map_file(file_path: &str) -> Result { let file = File::open(file_path) .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?; unsafe { let mapped_file = Mmap::map(&file).map_err(|errmsg| { MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg)) })?; Ok(mapped_file) } } /// Get a mapped storage file given the container and file type /// /// # Safety /// /// The memory mapped file may have undefined behavior if there are writes to this /// file after being mapped. Ensure no writes can happen to this file while this /// mapping stays alive. pub unsafe fn get_mapped_file( storage_dir: &str, container: &str, file_type: StorageFileType, ) -> Result { let storage_file = match file_type { StorageFileType::PackageMap => { String::from(storage_dir) + "/maps/" + container + ".package.map" } StorageFileType::FlagMap => String::from(storage_dir) + "/maps/" + container + ".flag.map", StorageFileType::FlagVal => String::from(storage_dir) + "/boot/" + container + ".val", StorageFileType::FlagInfo => String::from(storage_dir) + "/boot/" + container + ".info", }; if std::fs::metadata(&storage_file).is_err() { return Err(StorageFileNotFound(anyhow!("storage file {} does not exist", storage_file))); } unsafe { map_file(&storage_file) } } #[cfg(test)] mod tests { use super::*; use rand::Rng; use std::fs; use std::io::Read; fn map_and_verify(storage_dir: &str, file_type: StorageFileType, actual_file: &str) { let mut opened_file = File::open(actual_file).unwrap(); let mut content = Vec::new(); opened_file.read_to_end(&mut content).unwrap(); let mmaped_file = unsafe { get_mapped_file(storage_dir, "mockup", file_type).unwrap() }; assert_eq!(mmaped_file[..], content[..]); } fn create_test_storage_files() -> String { let mut rng = rand::thread_rng(); let number: u32 = rng.gen(); let storage_dir = String::from("/tmp/") + &number.to_string(); if std::fs::metadata(&storage_dir).is_ok() { fs::remove_dir_all(&storage_dir).unwrap(); } let maps_dir = storage_dir.clone() + "/maps"; let boot_dir = storage_dir.clone() + "/boot"; fs::create_dir(&storage_dir).unwrap(); fs::create_dir(&maps_dir).unwrap(); fs::create_dir(&boot_dir).unwrap(); let package_map = storage_dir.clone() + "/maps/mockup.package.map"; let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap(); fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap(); fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap(); fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap(); return storage_dir; } #[test] fn test_mapped_file_contents() { let storage_dir = create_test_storage_files(); map_and_verify(&storage_dir, StorageFileType::PackageMap, "./tests/data/v1/package_v1.map"); map_and_verify(&storage_dir, StorageFileType::FlagMap, "./tests/data/v1/flag_v1.map"); map_and_verify(&storage_dir, StorageFileType::FlagVal, "./tests/data/v1/flag_v1.val"); map_and_verify(&storage_dir, StorageFileType::FlagInfo, "./tests/data/v1/flag_v1.info"); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! package table query module defines the package table file read from mapped bytes use crate::AconfigStorageError; use aconfig_storage_file::{ package_table::PackageTableHeader, package_table::PackageTableNode, read_u32_from_bytes, MAX_SUPPORTED_FILE_VERSION, }; use anyhow::anyhow; /// Package table query return #[derive(PartialEq, Debug)] pub struct PackageReadContext { pub package_id: u32, pub boolean_start_index: u32, pub fingerprint: u64, } /// Query package read context: package id and start index pub fn find_package_read_context( buf: &[u8], package: &str, ) -> Result, AconfigStorageError> { let interpreted_header = PackageTableHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot read storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4; let bucket_index = PackageTableNode::find_bucket_index(package, num_buckets); let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize; let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize; if package_node_offset < interpreted_header.node_offset as usize || package_node_offset >= interpreted_header.file_size as usize { return Ok(None); } loop { let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..], interpreted_header.version)?; if interpreted_node.package_name == package { return Ok(Some(PackageReadContext { package_id: interpreted_node.package_id, boolean_start_index: interpreted_node.boolean_start_index, fingerprint: interpreted_node.fingerprint, })); } match interpreted_node.next_offset { Some(offset) => package_node_offset = offset as usize, None => return Ok(None), } } } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION}; #[test] // this test point locks down table query fn test_package_query() { let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes(); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); } #[test] // this test point locks down table query fn test_package_query_v2() { let package_table = create_test_package_table(2).into_bytes(); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 15248948510590158086u64, }; assert_eq!(package_context, expected_package_context); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 4431940502274857964u64, }; assert_eq!(package_context, expected_package_context); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 16233229917711622375u64, }; assert_eq!(package_context, expected_package_context); } #[test] // this test point locks down table query of a non exist package fn test_not_existed_package_query() { // this will land at an empty bucket let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes(); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_3") .unwrap(); assert_eq!(package_context, None); // this will land at the end of a linked list let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_5") .unwrap(); assert_eq!(package_context, None); } #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { let mut table = create_test_package_table(DEFAULT_FILE_VERSION); table.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let package_table = table.into_bytes(); let error = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1") .unwrap_err(); assert_eq!( format!("{:?}", error), format!( "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})", MAX_SUPPORTED_FILE_VERSION + 1, MAX_SUPPORTED_FILE_VERSION ) ); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigStorageReadAPI.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage; import dalvik.annotation.optimization.FastNative; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class AconfigStorageReadAPI { // Storage file dir on device private static final String STORAGEDIR = "/metadata/aconfig"; // Stoarge file type public enum StorageFileType { PACKAGE_MAP, FLAG_MAP, FLAG_VAL, FLAG_INFO } // Map a storage file given file path public static MappedByteBuffer mapStorageFile(String file) throws IOException { FileInputStream stream = new FileInputStream(file); FileChannel channel = stream.getChannel(); return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); } // Map a storage file given container and file type public static MappedByteBuffer getMappedFile(String container, StorageFileType type) throws IOException { switch (type) { case PACKAGE_MAP: return mapStorageFile(STORAGEDIR + "/maps/" + container + ".package.map"); case FLAG_MAP: return mapStorageFile(STORAGEDIR + "/maps/" + container + ".flag.map"); case FLAG_VAL: return mapStorageFile(STORAGEDIR + "/boot/" + container + ".val"); case FLAG_INFO: return mapStorageFile(STORAGEDIR + "/boot/" + container + ".info"); default: throw new IOException("Invalid storage file type"); } } // JNI interface to get package read context // @param mappedFile: memory mapped package map file // @param packageName: package name // @throws IOException if the passed in file is not a valid package map file @FastNative private static native ByteBuffer getPackageReadContextImpl( ByteBuffer mappedFile, String packageName) throws IOException; // API to get package read context // @param mappedFile: memory mapped package map file // @param packageName: package name // @throws IOException if the passed in file is not a valid package map file public static PackageReadContext getPackageReadContext( ByteBuffer mappedFile, String packageName) throws IOException { ByteBuffer buffer = getPackageReadContextImpl(mappedFile, packageName); buffer.order(ByteOrder.LITTLE_ENDIAN); return new PackageReadContext(buffer.getInt(), buffer.getInt(4)); } // JNI interface to get flag read context // @param mappedFile: memory mapped flag map file // @param packageId: package id to represent a specific package, obtained from // package map file // @param flagName: flag name // @throws IOException if the passed in file is not a valid flag map file @FastNative private static native ByteBuffer getFlagReadContextImpl( ByteBuffer mappedFile, int packageId, String flagName) throws IOException; // API to get flag read context // @param mappedFile: memory mapped flag map file // @param packageId: package id to represent a specific package, obtained from // package map file // @param flagName: flag name // @throws IOException if the passed in file is not a valid flag map file public static FlagReadContext getFlagReadContext( ByteBuffer mappedFile, int packageId, String flagName) throws IOException { ByteBuffer buffer = getFlagReadContextImpl(mappedFile, packageId, flagName); buffer.order(ByteOrder.LITTLE_ENDIAN); return new FlagReadContext(buffer.getInt(), buffer.getInt(4)); } // JNI interface to get boolean flag value // @param mappedFile: memory mapped flag value file // @param flagIndex: flag global index in the flag value array // @throws IOException if the passed in file is not a valid flag value file or the // flag index went over the file boundary. @FastNative public static native boolean getBooleanFlagValue(ByteBuffer mappedFile, int flagIndex) throws IOException; @FastNative public static native long hash(String packageName) throws IOException; static { System.loadLibrary("aconfig_storage_read_api_rust_jni"); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/FlagReadContext.java ================================================ package android.aconfig.storage; /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class FlagReadContext { public StoredFlagType mFlagType; public int mFlagIndex; public FlagReadContext(int flagType, int flagIndex) { mFlagType = StoredFlagType.fromInteger(flagType); mFlagIndex = flagIndex; } // Flag type enum, consistent with the definition in aconfig_storage_file/src/lib.rs public enum StoredFlagType { ReadWriteBoolean, ReadOnlyBoolean, FixedReadOnlyBoolean; public static StoredFlagType fromInteger(int x) { switch(x) { case 0: return ReadWriteBoolean; case 1: return ReadOnlyBoolean; case 2: return FixedReadOnlyBoolean; default: return null; } } } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/PackageReadContext.java ================================================ package android.aconfig.storage; /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class PackageReadContext { public int mPackageId; public int mBooleanStartIndex; public PackageReadContext(int packageId, int booleanStartIndex) { mPackageId = packageId; mBooleanStartIndex = booleanStartIndex; } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; import static android.aconfig.storage.TableUtils.StorageFilesBundle; import android.aconfig.storage.AconfigStorageException; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; import java.io.Closeable; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * An {@code aconfig} package containing the enabled state of its flags. * *

Note: this is intended only to be used by generated code. To determine if a given flag * is enabled in app code, the generated android flags should be used. * *

This class is used to read the flag from platform Aconfig Package.Each instance of this class * will cache information related to one package. To read flags from a different package, a new * instance of this class should be {@link #load loaded}. * * @hide */ public class PlatformAconfigPackage { private static final String TAG = "PlatformAconfigPackage"; private static final String MAP_PATH = "/metadata/aconfig/maps/"; private static final String BOOT_PATH = "/metadata/aconfig/boot/"; private FlagTable mFlagTable; private FlagValueList mFlagValueList; private int mPackageBooleanStartOffset = -1; private int mPackageId = -1; private PlatformAconfigPackage() {} /** @hide */ static final Map sStorageFilesCache = new HashMap<>(); /** @hide */ @UnsupportedAppUsage public static final Set PLATFORM_PACKAGE_MAP_FILES = Set.of( "system.package.map", "system_ext.package.map", "vendor.package.map", "product.package.map"); static { for (String pf : PLATFORM_PACKAGE_MAP_FILES) { try { PackageTable pTable = PackageTable.fromBytes(mapStorageFile(MAP_PATH + pf)); String container = pTable.getHeader().getContainer(); FlagTable fTable = FlagTable.fromBytes(mapStorageFile(MAP_PATH + container + ".flag.map")); FlagValueList fValueList = FlagValueList.fromBytes(mapStorageFile(BOOT_PATH + container + ".val")); StorageFilesBundle files = new StorageFilesBundle(pTable, fTable, fValueList); for (String packageName : pTable.getPackageList()) { sStorageFilesCache.put(packageName, files); } } catch (Exception e) { // pass Log.w(TAG, e.toString()); } } } /** * Loads a platform Aconfig Package from Aconfig Storage. * *

This method attempts to load the specified platform Aconfig package. * * @param packageName The name of the Aconfig package to load. * @return An instance of {@link PlatformAconfigPackage}, which may be empty if the package is * not found in the container. Null if the package is not found in platform partitions. * @throws AconfigStorageReadException if there is an error reading from Aconfig Storage, such * as if the storage system is not found, or there is an error reading the storage file. The * specific error code can be got using {@link AconfigStorageReadException#getErrorCode()}. * @hide */ @UnsupportedAppUsage public static PlatformAconfigPackage load(String packageName) { try { PlatformAconfigPackage aconfigPackage = new PlatformAconfigPackage(); StorageFilesBundle files = sStorageFilesCache.get(packageName); if (files == null) { return null; } PackageTable.Node pNode = files.packageTable.get(packageName); aconfigPackage.mFlagTable = files.flagTable; aconfigPackage.mFlagValueList = files.flagValueList; aconfigPackage.mPackageBooleanStartOffset = pNode.getBooleanStartIndex(); aconfigPackage.mPackageId = pNode.getPackageId(); return aconfigPackage; } catch (AconfigStorageException e) { throw new AconfigStorageReadException( e.getErrorCode(), "Fail to create PlatformAconfigPackage: " + packageName, e); } catch (Exception e) { throw new AconfigStorageReadException( AconfigStorageReadException.ERROR_GENERIC, "Fail to create PlatformAconfigPackage: " + packageName, e); } } /** * Retrieves the value of a boolean flag. * *

This method retrieves the value of the specified flag. If the flag exists within the * loaded Aconfig Package, its value is returned. Otherwise, the provided `defaultValue` is * returned. * * @param flagName The name of the flag (excluding any package name prefix). * @param defaultValue The value to return if the flag is not found. * @return The boolean value of the flag, or `defaultValue` if the flag is not found. * @hide */ @UnsupportedAppUsage public boolean getBooleanFlagValue(String flagName, boolean defaultValue) { FlagTable.Node fNode = mFlagTable.get(mPackageId, flagName); if (fNode == null) { return defaultValue; } return mFlagValueList.getBoolean(fNode.getFlagIndex() + mPackageBooleanStartOffset); } // Map a storage file given file path private static MappedByteBuffer mapStorageFile(String file) { FileChannel channel = null; try { channel = FileChannel.open(Paths.get(file), StandardOpenOption.READ); return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); } catch (Exception e) { throw new AconfigStorageReadException( AconfigStorageReadException.ERROR_CANNOT_READ_STORAGE_FILE, "Fail to mmap storage", e); } finally { quietlyDispose(channel); } } private static void quietlyDispose(Closeable closable) { try { if (closable != null) { closable.close(); } } catch (Exception e) { // no need to care, at least as of now } } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackageInternal.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; import static android.aconfig.storage.TableUtils.StorageFilesBundle; import android.aconfig.storage.AconfigStorageException; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import android.compat.annotation.UnsupportedAppUsage; /** * An {@code aconfig} package containing the enabled state of its flags. * *

Note: this is intended only to be used by generated code. To determine if a given flag * is enabled in app code, the generated android flags should be used. * *

This class is not part of the public API and should be used by Acnofig Flag internally It * is intended for internal use only and will be changed or removed without notice. * *

This class is used to read the flag from Aconfig Package.Each instance of this class will * cache information related to one package. To read flags from a different package, a new instance * of this class should be {@link #load loaded}. * * @hide */ public class PlatformAconfigPackageInternal { private final FlagValueList mFlagValueList; private final int mPackageBooleanStartOffset; private PlatformAconfigPackageInternal( FlagValueList flagValueList, int packageBooleanStartOffset) { this.mFlagValueList = flagValueList; this.mPackageBooleanStartOffset = packageBooleanStartOffset; } /** * Loads an Aconfig package from the specified container and verifies its fingerprint. * *

This method is intended for internal use only and may be changed or removed without * notice. * * @param packageName The name of the Aconfig package. * @param packageFingerprint The expected fingerprint of the package. * @return An instance of {@link PlatformAconfigPackageInternal} representing the loaded * package. * @hide */ @UnsupportedAppUsage public static PlatformAconfigPackageInternal load(String packageName, long packageFingerprint) { StorageFilesBundle files = PlatformAconfigPackage.sStorageFilesCache.get(packageName); if (files == null) { throw new AconfigStorageException( AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, "package " + packageName + " cannot be found on the device"); } PackageTable.Node pNode = files.packageTable.get(packageName); FlagValueList vList = files.flagValueList; if (pNode.hasPackageFingerprint() && packageFingerprint != pNode.getPackageFingerprint()) { throw new AconfigStorageException( AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH, "package " + packageName + "fingerprint doesn't match the one on device"); } return new PlatformAconfigPackageInternal(vList, pNode.getBooleanStartIndex()); } /** * Retrieves the value of a boolean flag using its index. * *

This method is intended for internal use only and may be changed or removed without * notice. * *

This method retrieves the value of a flag within the loaded Aconfig package using its * index. The index is generated at build time and may vary between builds. * * @param index The index of the flag within the package. * @return The boolean value of the flag. * @hide */ @UnsupportedAppUsage public boolean getBooleanFlagValue(int index) { return mFlagValueList.getBoolean(index + mPackageBooleanStartOffset); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/srcs/lib.rs ================================================ //! aconfig storage read api java rust interlop use aconfig_storage_file::SipHasher13; use aconfig_storage_read_api::flag_table_query::find_flag_read_context; use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value; use aconfig_storage_read_api::package_table_query::find_package_read_context; use aconfig_storage_read_api::{FlagReadContext, PackageReadContext}; use anyhow::Result; use jni::objects::{JByteBuffer, JClass, JString}; use jni::sys::{jboolean, jint, jlong}; use jni::JNIEnv; use std::hash::Hasher; /// Call rust find package read context fn get_package_read_context_java( env: &mut JNIEnv, file: JByteBuffer, package: JString, ) -> Result> { // SAFETY: // The safety here is ensured as the package name is guaranteed to be a java string let package_name: String = unsafe { env.get_string_unchecked(&package)?.into() }; let buffer_ptr = env.get_direct_buffer_address(&file)?; let buffer_size = env.get_direct_buffer_capacity(&file)?; // SAFETY: // The safety here is ensured as only non null MemoryMappedBuffer will be passed in, // so the conversion to slice is guaranteed to be valid let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) }; Ok(find_package_read_context(buffer, &package_name)?) } /// Get package read context JNI #[no_mangle] #[allow(unused)] pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getPackageReadContextImpl< 'local, >( mut env: JNIEnv<'local>, class: JClass<'local>, file: JByteBuffer<'local>, package: JString<'local>, ) -> JByteBuffer<'local> { let mut package_id = -1; let mut boolean_start_index = -1; match get_package_read_context_java(&mut env, file, package) { Ok(context_opt) => { if let Some(context) = context_opt { package_id = context.package_id as i32; boolean_start_index = context.boolean_start_index as i32; } } Err(errmsg) => { env.throw(("java/io/IOException", errmsg.to_string())).expect("failed to throw"); } } let mut bytes = Vec::new(); bytes.extend_from_slice(&package_id.to_le_bytes()); bytes.extend_from_slice(&boolean_start_index.to_le_bytes()); let (addr, len) = { let buf = bytes.leak(); (buf.as_mut_ptr(), buf.len()) }; // SAFETY: // The safety here is ensured as the content is ensured to be valid unsafe { env.new_direct_byte_buffer(addr, len).expect("failed to create byte buffer") } } /// Call rust find flag read context fn get_flag_read_context_java( env: &mut JNIEnv, file: JByteBuffer, package_id: jint, flag: JString, ) -> Result> { // SAFETY: // The safety here is ensured as the flag name is guaranteed to be a java string let flag_name: String = unsafe { env.get_string_unchecked(&flag)?.into() }; let buffer_ptr = env.get_direct_buffer_address(&file)?; let buffer_size = env.get_direct_buffer_capacity(&file)?; // SAFETY: // The safety here is ensured as only non null MemoryMappedBuffer will be passed in, // so the conversion to slice is guaranteed to be valid let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) }; Ok(find_flag_read_context(buffer, package_id as u32, &flag_name)?) } /// Get flag read context JNI #[no_mangle] #[allow(unused)] pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getFlagReadContextImpl< 'local, >( mut env: JNIEnv<'local>, class: JClass<'local>, file: JByteBuffer<'local>, package_id: jint, flag: JString<'local>, ) -> JByteBuffer<'local> { let mut flag_type = -1; let mut flag_index = -1; match get_flag_read_context_java(&mut env, file, package_id, flag) { Ok(context_opt) => { if let Some(context) = context_opt { flag_type = context.flag_type as i32; flag_index = context.flag_index as i32; } } Err(errmsg) => { env.throw(("java/io/IOException", errmsg.to_string())).expect("failed to throw"); } } let mut bytes = Vec::new(); bytes.extend_from_slice(&flag_type.to_le_bytes()); bytes.extend_from_slice(&flag_index.to_le_bytes()); let (addr, len) = { let buf = bytes.leak(); (buf.as_mut_ptr(), buf.len()) }; // SAFETY: // The safety here is ensured as the content is ensured to be valid unsafe { env.new_direct_byte_buffer(addr, len).expect("failed to create byte buffer") } } /// Call rust find boolean flag value fn get_boolean_flag_value_java( env: &mut JNIEnv, file: JByteBuffer, flag_index: jint, ) -> Result { let buffer_ptr = env.get_direct_buffer_address(&file)?; let buffer_size = env.get_direct_buffer_capacity(&file)?; // SAFETY: // The safety here is ensured as only non null MemoryMappedBuffer will be passed in, // so the conversion to slice is guaranteed to be valid let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) }; Ok(find_boolean_flag_value(buffer, flag_index as u32)?) } /// Get flag value JNI #[no_mangle] #[allow(unused)] pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_getBooleanFlagValue< 'local, >( mut env: JNIEnv<'local>, class: JClass<'local>, file: JByteBuffer<'local>, flag_index: jint, ) -> jboolean { match get_boolean_flag_value_java(&mut env, file, flag_index) { Ok(value) => value as u8, Err(errmsg) => { env.throw(("java/io/IOException", errmsg.to_string())).expect("failed to throw"); 0u8 } } } /// Get flag value JNI #[no_mangle] #[allow(unused)] pub extern "system" fn Java_android_aconfig_storage_AconfigStorageReadAPI_hash<'local>( mut env: JNIEnv<'local>, class: JClass<'local>, package_name: JString<'local>, ) -> jlong { match siphasher13_hash(&mut env, package_name) { Ok(value) => value as jlong, Err(errmsg) => { env.throw(("java/io/IOException", errmsg.to_string())).expect("failed to throw"); 0i64 } } } fn siphasher13_hash(env: &mut JNIEnv, package_name: JString) -> Result { // SAFETY: // The safety here is ensured as the flag name is guaranteed to be a java string let flag_name: String = unsafe { env.get_string_unchecked(&package_name)?.into() }; let mut s = SipHasher13::new(); s.write(flag_name.as_bytes()); s.write_u8(0xff); Ok(s.finish()) } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/AconfigStorageReadFunctionalTest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/Android.bp ================================================ filegroup { name: "read_api_test_storage_files", srcs: [ "data/v1/package_v1.map", "data/v1/flag_v1.map", "data/v1/flag_v1.val", "data/v1/flag_v1.info", "data/v2/package_v2.map", "data/v2/flag_v2.map", "data/v2/flag_v2.val", "data/v2/flag_v2.info", ], } rust_test { name: "aconfig_storage_read_api.test.rust", srcs: [ "storage_read_api_test.rs", ], rustlibs: [ "libanyhow", "libaconfig_storage_file", "libaconfig_storage_read_api", "librand", ], data: [ ":read_api_test_storage_files", ], test_suites: ["general-tests"], } cc_test { name: "aconfig_storage_read_api.test.cpp", srcs: [ "storage_read_api_test.cpp", ], static_libs: [ "libgmock", "libaconfig_storage_read_api_cc", "libbase", "liblog", ], data: [ ":read_api_test_storage_files", ], test_suites: [ "device-tests", "general-tests", ], } android_test { name: "aconfig_storage_read_functional", srcs: [ "functional/srcs/**/*.java", ], static_libs: [ "aconfig_device_paths_java_util", "aconfig_storage_file_java", "androidx.test.rules", "libaconfig_storage_read_api_java", "junit", ], jni_libs: [ "libaconfig_storage_read_api_rust_jni", ], data: [ ":read_api_test_storage_files", ], platform_apis: true, certificate: "platform", test_suites: [ "general-tests", ], test_config: "AconfigStorageReadFunctionalTest.xml", team: "trendy_team_android_core_experiments", } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/AndroidManifest.xml ================================================ ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/AconfigStorageReadAPITest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.aconfig.DeviceProtosTestUtil; import android.aconfig.nano.Aconfig.parsed_flag; import android.aconfig.storage.AconfigStorageReadAPI; import android.aconfig.storage.FlagReadContext; import android.aconfig.storage.FlagReadContext.StoredFlagType; import android.aconfig.storage.PackageReadContext; import android.aconfig.storage.SipHasher13; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; import java.nio.MappedByteBuffer; import java.util.ArrayList; import java.util.List; @RunWith(JUnit4.class) public class AconfigStorageReadAPITest { private String mStorageDir = "/data/local/tmp/aconfig_java_api_test"; @Test public void testPackageContextQuery() { MappedByteBuffer packageMap = null; try { packageMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.package.map"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(packageMap != null); try { PackageReadContext context = AconfigStorageReadAPI.getPackageReadContext( packageMap, "com.android.aconfig.storage.test_1"); assertEquals(context.mPackageId, 0); assertEquals(context.mBooleanStartIndex, 0); context = AconfigStorageReadAPI.getPackageReadContext( packageMap, "com.android.aconfig.storage.test_2"); assertEquals(context.mPackageId, 1); assertEquals(context.mBooleanStartIndex, 3); context = AconfigStorageReadAPI.getPackageReadContext( packageMap, "com.android.aconfig.storage.test_4"); assertEquals(context.mPackageId, 2); assertEquals(context.mBooleanStartIndex, 6); } catch (IOException ex) { assertTrue(ex.toString(), false); } } @Test public void testNonExistPackageContextQuery() { MappedByteBuffer packageMap = null; try { packageMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.package.map"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(packageMap != null); try { PackageReadContext context = AconfigStorageReadAPI.getPackageReadContext(packageMap, "unknown"); assertEquals(context.mPackageId, -1); assertEquals(context.mBooleanStartIndex, -1); } catch (IOException ex) { assertTrue(ex.toString(), false); } } @Test public void testFlagContextQuery() { MappedByteBuffer flagMap = null; try { flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.flag.map"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(flagMap != null); class Baseline { public int mPackageId; public String mFlagName; public StoredFlagType mFlagType; public int mFlagIndex; public Baseline( int packageId, String flagName, StoredFlagType flagType, int flagIndex) { mPackageId = packageId; mFlagName = flagName; mFlagType = flagType; mFlagIndex = flagIndex; } } List baselines = new ArrayList(); baselines.add(new Baseline(0, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 1)); baselines.add(new Baseline(0, "enabled_rw", StoredFlagType.ReadWriteBoolean, 2)); baselines.add(new Baseline(2, "enabled_rw", StoredFlagType.ReadWriteBoolean, 1)); baselines.add(new Baseline(1, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0)); baselines.add(new Baseline(1, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 1)); baselines.add(new Baseline(1, "enabled_ro", StoredFlagType.ReadOnlyBoolean, 2)); baselines.add(new Baseline(2, "enabled_fixed_ro", StoredFlagType.FixedReadOnlyBoolean, 0)); baselines.add(new Baseline(0, "disabled_rw", StoredFlagType.ReadWriteBoolean, 0)); try { for (Baseline baseline : baselines) { FlagReadContext context = AconfigStorageReadAPI.getFlagReadContext( flagMap, baseline.mPackageId, baseline.mFlagName); assertEquals(context.mFlagType, baseline.mFlagType); assertEquals(context.mFlagIndex, baseline.mFlagIndex); } } catch (IOException ex) { assertTrue(ex.toString(), false); } } @Test public void testNonExistFlagContextQuery() { MappedByteBuffer flagMap = null; try { flagMap = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/maps/mockup.flag.map"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(flagMap != null); try { FlagReadContext context = AconfigStorageReadAPI.getFlagReadContext(flagMap, 0, "unknown"); assertEquals(context.mFlagType, null); assertEquals(context.mFlagIndex, -1); context = AconfigStorageReadAPI.getFlagReadContext(flagMap, 3, "enabled_ro"); assertEquals(context.mFlagType, null); assertEquals(context.mFlagIndex, -1); } catch (IOException ex) { assertTrue(ex.toString(), false); } } @Test public void testBooleanFlagValueQuery() { MappedByteBuffer flagVal = null; try { flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/boot/mockup.val"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(flagVal != null); boolean[] baselines = {false, true, true, false, true, true, true, true}; for (int i = 0; i < 8; ++i) { try { Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, i); assertEquals(value, baselines[i]); } catch (IOException ex) { assertTrue(ex.toString(), false); } } } @Test public void testInvalidBooleanFlagValueQuery() { MappedByteBuffer flagVal = null; try { flagVal = AconfigStorageReadAPI.mapStorageFile(mStorageDir + "/boot/mockup.val"); } catch (IOException ex) { assertTrue(ex.toString(), false); } assertTrue(flagVal != null); try { Boolean value = AconfigStorageReadAPI.getBooleanFlagValue(flagVal, 9); assertTrue("should throw", false); } catch (IOException ex) { String expectedErrmsg = "invalid storage file byte offset"; assertTrue(ex.toString(), ex.toString().contains(expectedErrmsg)); } } @Test public void testRustJavaEqualHash() throws IOException { List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); for (parsed_flag flag : flags) { String packageName = flag.package_; String flagName = flag.name; long rHash = AconfigStorageReadAPI.hash(packageName); long jHash = SipHasher13.hash(packageName.getBytes()); assertEquals(rHash, jHash); String fullFlagName = packageName + "/" + flagName; rHash = AconfigStorageReadAPI.hash(fullFlagName); jHash = SipHasher13.hash(fullFlagName.getBytes()); assertEquals(rHash, jHash); } } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import android.aconfig.DeviceProtosTestUtil; import android.aconfig.nano.Aconfig; import android.aconfig.nano.Aconfig.parsed_flag; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import android.aconfig.storage.StorageFileProvider; import android.internal.aconfig.storage.AconfigStorageException; import android.os.flagging.PlatformAconfigPackageInternal; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @RunWith(JUnit4.class) public class PlatformAconfigPackageInternalTest { private static final Set PLATFORM_CONTAINERS = Set.of("system", "system_ext", "vendor", "product"); @Test public void testPlatformAconfigPackageInternal_load() throws IOException { List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); Map readerMap = new HashMap<>(); StorageFileProvider fp = StorageFileProvider.getDefaultProvider(); for (parsed_flag flag : flags) { if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) { continue; } String container = flag.container; String packageName = flag.package_; String flagName = flag.name; if (!PLATFORM_CONTAINERS.contains(container)) continue; PackageTable pTable = fp.getPackageTable(container); PackageTable.Node pNode = pTable.get(packageName); FlagTable fTable = fp.getFlagTable(container); FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName); FlagValueList fList = fp.getFlagValueList(container); int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex(); boolean rVal = fList.getBoolean(index); long fingerprint = pNode.getPackageFingerprint(); PlatformAconfigPackageInternal reader = readerMap.get(packageName); if (reader == null) { reader = PlatformAconfigPackageInternal.load(packageName, fingerprint); readerMap.put(packageName, reader); } boolean jVal = reader.getBooleanFlagValue(fNode.getFlagIndex()); assertEquals(rVal, jVal); } } @Test public void testPlatformAconfigPackage_load_withError() throws IOException { // package not found AconfigStorageException e = assertThrows( AconfigStorageException.class, () -> PlatformAconfigPackageInternal.load("fake_package", 0)); assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode()); // fingerprint doesn't match List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); StorageFileProvider fp = StorageFileProvider.getDefaultProvider(); parsed_flag flag = flags.get(0); String container = flag.container; String packageName = flag.package_; boolean value = flag.state == Aconfig.ENABLED; PackageTable pTable = fp.getPackageTable(container); PackageTable.Node pNode = pTable.get(packageName); if (pNode.hasPackageFingerprint()) { long fingerprint = pNode.getPackageFingerprint(); e = assertThrows( AconfigStorageException.class, () -> PlatformAconfigPackageInternal.load( packageName, fingerprint + 1)); assertEquals(AconfigStorageException.ERROR_FILE_FINGERPRINT_MISMATCH, e.getErrorCode()); } } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import android.aconfig.DeviceProtosTestUtil; import android.aconfig.nano.Aconfig; import android.aconfig.nano.Aconfig.parsed_flag; import android.aconfig.storage.FlagTable; import android.aconfig.storage.FlagValueList; import android.aconfig.storage.PackageTable; import android.aconfig.storage.StorageFileProvider; import android.os.flagging.PlatformAconfigPackage; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @RunWith(JUnit4.class) public class PlatformAconfigPackageTest { private static final Set PLATFORM_CONTAINERS = Set.of("system", "system_ext", "vendor", "product"); @Test public void testPlatformAconfigPackage_StorageFilesCache() throws IOException { List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); for (parsed_flag flag : flags) { if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) { continue; } String container = flag.container; String packageName = flag.package_; if (!PLATFORM_CONTAINERS.contains(container)) continue; assertNotNull(PlatformAconfigPackage.load(packageName)); } } @Test public void testPlatformAconfigPackage_load() throws IOException { List flags = DeviceProtosTestUtil.loadAndParseFlagProtos(); Map readerMap = new HashMap<>(); StorageFileProvider fp = StorageFileProvider.getDefaultProvider(); for (parsed_flag flag : flags) { if (flag.permission == Aconfig.READ_ONLY && flag.state == Aconfig.DISABLED) { continue; } String container = flag.container; String packageName = flag.package_; String flagName = flag.name; if (!PLATFORM_CONTAINERS.contains(container)) continue; PackageTable pTable = fp.getPackageTable(container); PackageTable.Node pNode = pTable.get(packageName); FlagTable fTable = fp.getFlagTable(container); FlagTable.Node fNode = fTable.get(pNode.getPackageId(), flagName); FlagValueList fList = fp.getFlagValueList(container); int index = pNode.getBooleanStartIndex() + fNode.getFlagIndex(); boolean rVal = fList.getBoolean(index); long fingerprint = pNode.getPackageFingerprint(); PlatformAconfigPackage reader = readerMap.get(packageName); if (reader == null) { reader = PlatformAconfigPackage.load(packageName); readerMap.put(packageName, reader); } boolean jVal = reader.getBooleanFlagValue(flagName, !rVal); assertEquals(rVal, jVal); } } @Test public void testPlatformAconfigPackage_load_withError() throws IOException { // package not found assertNull(PlatformAconfigPackage.load("fake_container")); } } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "aconfig_storage/aconfig_storage_read_api.hpp" #include #include #include using namespace android::base; namespace api = aconfig_storage; namespace private_api = aconfig_storage::private_internal_api; class AconfigStorageTest : public ::testing::Test { protected: Result copy_file(std::string const& src_file, std::string const& dst_file) { auto content = std::string(); if (!ReadFileToString(src_file, &content)) { return Error() << "failed to read file: " << src_file; } if (!WriteStringToFile(content, dst_file)) { return Error() << "failed to copy file: " << dst_file; } return {}; } void SetUp() override { auto const test_base_dir = android::base::GetExecutableDirectory(); auto const test_dir = test_base_dir + "/data/v1"; storage_dir = std::string(root_dir.path); auto maps_dir = storage_dir + "/maps"; auto boot_dir = storage_dir + "/boot"; mkdir(maps_dir.c_str(), 0775); mkdir(boot_dir.c_str(), 0775); package_map = std::string(maps_dir) + "/mockup.package.map"; flag_map = std::string(maps_dir) + "/mockup.flag.map"; flag_val = std::string(boot_dir) + "/mockup.val"; flag_info = std::string(boot_dir) + "/mockup.info"; copy_file(test_dir + "/package_v1.map", package_map); copy_file(test_dir + "/flag_v1.map", flag_map); copy_file(test_dir + "/flag_v1.val", flag_val); copy_file(test_dir + "/flag_v1.info", flag_info); } void TearDown() override { std::remove(package_map.c_str()); std::remove(flag_map.c_str()); std::remove(flag_val.c_str()); std::remove(flag_info.c_str()); } TemporaryDir root_dir; std::string storage_dir; std::string package_map; std::string flag_map; std::string flag_val; std::string flag_info; }; /// Test to lock down storage file version query api TEST_F(AconfigStorageTest, test_storage_version_query) { auto version = api::get_storage_file_version(package_map); ASSERT_TRUE(version.ok()); ASSERT_EQ(*version, 1); version = api::get_storage_file_version(flag_map); ASSERT_TRUE(version.ok()); ASSERT_EQ(*version, 1); version = api::get_storage_file_version(flag_val); ASSERT_TRUE(version.ok()); ASSERT_EQ(*version, 1); version = api::get_storage_file_version(flag_info); ASSERT_TRUE(version.ok()); ASSERT_EQ(*version, 1); } /// Negative test to lock down the error when mapping none exist storage files TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "vendor", api::StorageFileType::package_map); ASSERT_FALSE(mapped_file_result.ok()); ASSERT_EQ(mapped_file_result.error(), std::string("failed to open ") + storage_dir + "/maps/vendor.package.map: No such file or directory"); } /// Test to lock down storage package context query api TEST_F(AconfigStorageTest, test_package_context_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::package_map); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto context = api::get_package_read_context( *mapped_file, "com.android.aconfig.storage.test_1"); ASSERT_TRUE(context.ok()); ASSERT_TRUE(context->package_exists); ASSERT_EQ(context->package_id, 0); ASSERT_EQ(context->boolean_start_index, 0); context = api::get_package_read_context( *mapped_file, "com.android.aconfig.storage.test_2"); ASSERT_TRUE(context.ok()); ASSERT_TRUE(context->package_exists); ASSERT_EQ(context->package_id, 1); ASSERT_EQ(context->boolean_start_index, 3); context = api::get_package_read_context( *mapped_file, "com.android.aconfig.storage.test_4"); ASSERT_TRUE(context.ok()); ASSERT_TRUE(context->package_exists); ASSERT_EQ(context->package_id, 2); ASSERT_EQ(context->boolean_start_index, 6); } /// Test to lock down when querying none exist package TEST_F(AconfigStorageTest, test_none_existent_package_context_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::package_map); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto context = api::get_package_read_context( *mapped_file, "com.android.aconfig.storage.test_3"); ASSERT_TRUE(context.ok()); ASSERT_FALSE(context->package_exists); } /// Test to lock down storage flag context query api TEST_F(AconfigStorageTest, test_flag_context_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_map); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto baseline = std::vector>{ {0, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1}, {0, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 2}, {2, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 1}, {1, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0}, {1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1}, {1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2}, {2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0}, {0, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0}, }; for (auto const&[package_id, flag_name, flag_type, flag_index] : baseline) { auto context = api::get_flag_read_context(*mapped_file, package_id, flag_name); ASSERT_TRUE(context.ok()); ASSERT_TRUE(context->flag_exists); ASSERT_EQ(context->flag_type, flag_type); ASSERT_EQ(context->flag_index, flag_index); } } /// Test to lock down when querying none exist flag TEST_F(AconfigStorageTest, test_none_existent_flag_context_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_map); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto context = api::get_flag_read_context(*mapped_file, 0, "none_exist"); ASSERT_TRUE(context.ok()); ASSERT_FALSE(context->flag_exists); context = api::get_flag_read_context(*mapped_file, 3, "enabled_ro"); ASSERT_TRUE(context.ok()); ASSERT_FALSE(context->flag_exists); } /// Test to lock down storage flag value query api TEST_F(AconfigStorageTest, test_boolean_flag_value_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_val); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto expected_value = std::vector{ false, true, true, false, true, true, true, true}; for (int index = 0; index < 8; ++index) { auto value = api::get_boolean_flag_value(*mapped_file, index); ASSERT_TRUE(value.ok()); ASSERT_EQ(*value, expected_value[index]); } } /// Negative test to lock down the error when querying flag value out of range TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_val); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto value = api::get_boolean_flag_value(*mapped_file, 8); ASSERT_FALSE(value.ok()); ASSERT_EQ(value.error(), std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)")); } /// Test to lock down storage flag info query api TEST_F(AconfigStorageTest, test_boolean_flag_info_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_info); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto expected_value = std::vector{ true, false, true, true, false, false, false, true}; for (int index = 0; index < 8; ++index) { auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index); ASSERT_TRUE(attribute.ok()); ASSERT_EQ(*attribute & static_cast(api::FlagInfoBit::HasServerOverride), 0); ASSERT_EQ((*attribute & static_cast(api::FlagInfoBit::IsReadWrite)) != 0, expected_value[index]); ASSERT_EQ(*attribute & static_cast(api::FlagInfoBit::HasLocalOverride), 0); } } /// Negative test to lock down the error when querying flag info out of range TEST_F(AconfigStorageTest, test_invalid_boolean_flag_info_query) { auto mapped_file_result = private_api::get_mapped_file_impl( storage_dir, "mockup", api::StorageFileType::flag_info); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, 8); ASSERT_FALSE(attribute.ok()); ASSERT_EQ(attribute.error(), std::string("InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)")); } ================================================ FILE: tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs ================================================ #[cfg(not(feature = "cargo"))] mod aconfig_storage_rust_test { use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType, StoredFlagType}; use aconfig_storage_read_api::{ get_boolean_flag_value, get_flag_attribute, get_flag_read_context, get_package_read_context, get_storage_file_version, mapped_file::get_mapped_file, PackageReadContext, }; use rand::Rng; use std::fs; fn create_test_storage_files(version: u32) -> String { let mut rng = rand::thread_rng(); let number: u32 = rng.gen(); let storage_dir = String::from("/tmp/") + &number.to_string(); if std::fs::metadata(&storage_dir).is_ok() { fs::remove_dir_all(&storage_dir).unwrap(); } let maps_dir = storage_dir.clone() + "/maps"; let boot_dir = storage_dir.clone() + "/boot"; fs::create_dir(&storage_dir).unwrap(); fs::create_dir(maps_dir).unwrap(); fs::create_dir(boot_dir).unwrap(); let package_map = storage_dir.clone() + "/maps/mockup.package.map"; let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; fs::copy(format!("./data/v{0}/package_v{0}.map", version), package_map).unwrap(); fs::copy(format!("./data/v{0}/flag_v{0}.map", version), flag_map).unwrap(); fs::copy(format!("./data/v{}/flag_v{0}.val", version), flag_val).unwrap(); fs::copy(format!("./data/v{}/flag_v{0}.info", version), flag_info).unwrap(); storage_dir } #[test] fn test_unavailable_storage() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let err = unsafe { get_mapped_file(&storage_dir, "vendor", StorageFileType::PackageMap).unwrap_err() }; assert_eq!( format!("{:?}", err), format!( "StorageFileNotFound(storage file {}/maps/vendor.package.map does not exist)", storage_dir ) ); } #[test] fn test_package_context_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let package_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::PackageMap).unwrap() }; let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 0 }; assert_eq!(package_context, expected_package_context); } #[test] fn test_package_context_query_with_fingerprint() { let storage_dir = create_test_storage_files(2); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let package_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::PackageMap).unwrap() }; let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0, fingerprint: 15248948510590158086u64, }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3, fingerprint: 4431940502274857964u64, }; assert_eq!(package_context, expected_package_context); let package_context = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4") .unwrap() .unwrap(); let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6, fingerprint: 16233229917711622375u64, }; assert_eq!(package_context, expected_package_context); } #[test] fn test_none_exist_package_context_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let package_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::PackageMap).unwrap() }; let package_context_option = get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_3") .unwrap(); assert_eq!(package_context_option, None); } #[test] fn test_flag_context_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagMap).unwrap() }; let baseline = vec![ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16), (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16), (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16), (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16), (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16), (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16), (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16), ]; for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() { let flag_context = get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap(); assert_eq!(flag_context.flag_type, flag_type); assert_eq!(flag_context.flag_index, flag_index); } } #[test] fn test_none_exist_flag_context_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_mapped_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagMap).unwrap() }; let flag_context_option = get_flag_read_context(&flag_mapped_file, 0, "none_exist").unwrap(); assert_eq!(flag_context_option, None); let flag_context_option = get_flag_read_context(&flag_mapped_file, 3, "enabled_ro").unwrap(); assert_eq!(flag_context_option, None); } #[test] fn test_boolean_flag_value_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_value_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagVal).unwrap() }; let baseline: Vec = vec![false, true, true, false, true, true, true, true]; for (offset, expected_value) in baseline.into_iter().enumerate() { let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap(); assert_eq!(flag_value, expected_value); } } #[test] fn test_invalid_boolean_flag_value_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_value_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagVal).unwrap() }; let err = get_boolean_flag_value(&flag_value_file, 8u32).unwrap_err(); assert_eq!( format!("{:?}", err), "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)" ); } #[test] fn test_flag_info_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_info_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagInfo).unwrap() }; let is_rw: Vec = vec![true, false, true, true, false, false, false, true]; for (offset, expected_value) in is_rw.into_iter().enumerate() { let attribute = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap(); assert!((attribute & FlagInfoBit::HasServerOverride as u8) == 0u8); assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value); assert!((attribute & FlagInfoBit::HasLocalOverride as u8) == 0u8); } } #[test] fn test_invalid_boolean_flag_info_query() { let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_info_file = unsafe { get_mapped_file(&storage_dir, "mockup", StorageFileType::FlagInfo).unwrap() }; let err = get_flag_attribute(&flag_info_file, FlagValueType::Boolean, 8u32).unwrap_err(); assert_eq!( format!("{:?}", err), "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)" ); } #[test] fn test_storage_version_query_v1() { assert_eq!(get_storage_file_version("./data/v1/package_v1.map").unwrap(), 1); assert_eq!(get_storage_file_version("./data/v1/flag_v1.map").unwrap(), 1); assert_eq!(get_storage_file_version("./data/v1/flag_v1.val").unwrap(), 1); assert_eq!(get_storage_file_version("./data/v1/flag_v1.info").unwrap(), 1); } #[test] fn test_storage_version_query_v2() { assert_eq!(get_storage_file_version("./data/v2/package_v2.map").unwrap(), 2); assert_eq!(get_storage_file_version("./data/v2/flag_v2.map").unwrap(), 2); assert_eq!(get_storage_file_version("./data/v2/flag_v2.val").unwrap(), 2); assert_eq!(get_storage_file_version("./data/v2/flag_v2.info").unwrap(), 2); } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "aconfig_storage_write_api.defaults", edition: "2021", lints: "none", srcs: ["src/lib.rs"], rustlibs: [ "libanyhow", "libtempfile", "libmemmap2", "libcxx", "libthiserror", "libaconfig_storage_file", "libaconfig_storage_read_api", ], min_sdk_version: "34", apex_available: [ "//apex_available:anyapex", "//apex_available:platform", ], } rust_library { name: "libaconfig_storage_write_api", crate_name: "aconfig_storage_write_api", host_supported: true, defaults: ["aconfig_storage_write_api.defaults"], } rust_test_host { name: "aconfig_storage_write_api.test", test_suites: ["general-tests"], defaults: ["aconfig_storage_write_api.defaults"], data: [ "tests/flag.val", "tests/flag.info", ], rustlibs: [ "libaconfig_storage_read_api", ], } // cxx source codegen from rust api genrule { name: "libcxx_aconfig_storage_write_api_bridge_code", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.cc"], } // cxx header codegen from rust api genrule { name: "libcxx_aconfig_storage_write_api_bridge_header", tools: ["cxxbridge"], cmd: "$(location cxxbridge) $(in) --header > $(out)", srcs: ["src/lib.rs"], out: ["aconfig_storage/lib.rs.h"], } // a static cc lib based on generated code rust_ffi_static { name: "libaconfig_storage_write_api_cxx_bridge", crate_name: "aconfig_storage_write_api_cxx_bridge", host_supported: true, defaults: ["aconfig_storage_write_api.defaults"], } // flag write api cc interface cc_library_static { name: "libaconfig_storage_write_api_cc", srcs: ["aconfig_storage_write_api.cpp"], generated_headers: [ "cxx-bridge-header", "libcxx_aconfig_storage_write_api_bridge_header", ], generated_sources: ["libcxx_aconfig_storage_write_api_bridge_code"], whole_static_libs: ["libaconfig_storage_write_api_cxx_bridge"], export_include_dirs: ["include"], static_libs: [ "libaconfig_storage_read_api_cc", "libprotobuf-cpp-lite", "libbase", ], } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/Cargo.toml ================================================ [package] name = "aconfig_storage_write_api" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] anyhow = "1.0.69" cxx = "1.0" memmap2 = "0.8.0" tempfile = "3.9.0" thiserror = "1.0.56" aconfig_storage_file = { path = "../aconfig_storage_file" } aconfig_storage_read_api = { path = "../aconfig_storage_read_api" } [build-dependencies] cxx-build = "1.0" ================================================ FILE: tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp ================================================ #include #include #include #include #include #include #include "rust/cxx.h" #include "aconfig_storage/lib.rs.h" #include "aconfig_storage/aconfig_storage_write_api.hpp" namespace aconfig_storage { /// Map a storage file android::base::Result map_mutable_storage_file( std::string const &file) { struct stat file_stat; if (stat(file.c_str(), &file_stat) < 0) { return android::base::ErrnoError() << "stat failed"; } if ((file_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) { return android::base::Error() << "cannot map nonwriteable file"; } size_t file_size = file_stat.st_size; android::base::unique_fd ufd(open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC)); if (ufd.get() == -1) { return android::base::ErrnoError() << "failed to open " << file; }; void *const map_result = mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, ufd.get(), 0); if (map_result == MAP_FAILED) { return android::base::ErrnoError() << "mmap failed"; } auto mapped_file = new MutableMappedStorageFile(); mapped_file->file_ptr = map_result; mapped_file->file_size = file_size; return mapped_file; } /// Set boolean flag value android::base::Result set_boolean_flag_value( const MutableMappedStorageFile &file, uint32_t offset, bool value) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto update_cxx = update_boolean_flag_value_cxx(content, offset, value); if (!update_cxx.update_success) { return android::base::Error() << update_cxx.error_message.c_str(); } if (!msync(static_cast(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) { return android::base::ErrnoError() << "msync failed"; } return {}; } /// Set if flag has server override android::base::Result set_flag_has_server_override( const MutableMappedStorageFile &file, FlagValueType value_type, uint32_t offset, bool value) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto update_cxx = update_flag_has_server_override_cxx( content, static_cast(value_type), offset, value); if (!update_cxx.update_success) { return android::base::Error() << update_cxx.error_message.c_str(); } if (!msync(static_cast(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) { return android::base::ErrnoError() << "msync failed"; } return {}; } /// Set if flag has local override android::base::Result set_flag_has_local_override( const MutableMappedStorageFile &file, FlagValueType value_type, uint32_t offset, bool value) { auto content = rust::Slice( static_cast(file.file_ptr), file.file_size); auto update_cxx = update_flag_has_local_override_cxx( content, static_cast(value_type), offset, value); if (!update_cxx.update_success) { return android::base::Error() << update_cxx.error_message.c_str(); } if (!msync(static_cast(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) { return android::base::ErrnoError() << "msync failed"; } return {}; } } // namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_write_api/build.rs ================================================ fn main() { let _ = cxx_build::bridge("src/lib.rs"); println!("cargo:rerun-if-changed=src/lib.rs"); } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp ================================================ #pragma once #include #include #include #include namespace aconfig_storage { /// Mapped flag value file struct MutableMappedStorageFile : MappedStorageFile {}; /// Map a storage file android::base::Result map_mutable_storage_file( std::string const& file); /// Set boolean flag value android::base::Result set_boolean_flag_value( const MutableMappedStorageFile& file, uint32_t offset, bool value); /// Set if flag has server override android::base::Result set_flag_has_server_override( const MutableMappedStorageFile& file, FlagValueType value_type, uint32_t offset, bool value); /// Set if flag has local override android::base::Result set_flag_has_local_override( const MutableMappedStorageFile& file, FlagValueType value_type, uint32_t offset, bool value); } // namespace aconfig_storage ================================================ FILE: tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag info update module defines the flag info file write to mapped bytes use aconfig_storage_file::{ read_u8_from_bytes, AconfigStorageError, FlagInfoBit, FlagInfoHeader, FlagValueType, MAX_SUPPORTED_FILE_VERSION, }; use anyhow::anyhow; fn get_flag_info_offset( buf: &mut [u8], flag_type: FlagValueType, flag_index: u32, ) -> Result { let interpreted_header = FlagInfoHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot write to storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } // get byte offset to the flag info let head = match flag_type { FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize, }; if head >= interpreted_header.file_size as usize { return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!( "Flag value offset goes beyond the end of the file." ))); } Ok(head) } fn get_flag_attribute_and_offset( buf: &mut [u8], flag_type: FlagValueType, flag_index: u32, ) -> Result<(u8, usize), AconfigStorageError> { let head = get_flag_info_offset(buf, flag_type, flag_index)?; let mut pos = head; let attribute = read_u8_from_bytes(buf, &mut pos)?; Ok((attribute, head)) } /// Set if flag has server override pub fn update_flag_has_server_override( buf: &mut [u8], flag_type: FlagValueType, flag_index: u32, value: bool, ) -> Result { let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?; let has_override = (attribute & (FlagInfoBit::HasServerOverride as u8)) != 0; if has_override != value { buf[head] = (attribute ^ FlagInfoBit::HasServerOverride as u8).to_le_bytes()[0]; } Ok(head) } /// Set if flag has local override pub fn update_flag_has_local_override( buf: &mut [u8], flag_type: FlagValueType, flag_index: u32, value: bool, ) -> Result { let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?; let has_override = (attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0; if has_override != value { buf[head] = (attribute ^ FlagInfoBit::HasLocalOverride as u8).to_le_bytes()[0]; } Ok(head) } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION}; use aconfig_storage_read_api::flag_info_query::find_flag_attribute; #[test] // this test point locks down has server override update fn test_update_flag_has_server_override() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let mut buf = flag_info_list.into_bytes(); for i in 0..flag_info_list.header.num_flags { update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, true).unwrap(); let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap(); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0); update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, false).unwrap(); let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap(); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0); } } #[test] // this test point locks down has local override update fn test_update_flag_has_local_override() { let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let mut buf = flag_info_list.into_bytes(); for i in 0..flag_info_list.header.num_flags { update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, true).unwrap(); let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap(); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0); update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, false).unwrap(); let attribute = find_flag_attribute(&buf, FlagValueType::Boolean, i).unwrap(); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0); } } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! flag value update module defines the flag value file write to mapped bytes use aconfig_storage_file::{AconfigStorageError, FlagValueHeader, MAX_SUPPORTED_FILE_VERSION}; use anyhow::anyhow; /// Set flag value pub fn update_boolean_flag_value( buf: &mut [u8], flag_index: u32, flag_value: bool, ) -> Result { let interpreted_header = FlagValueHeader::from_bytes(buf)?; if interpreted_header.version > MAX_SUPPORTED_FILE_VERSION { return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( "Cannot write to storage file with a higher version of {} with lib version {}", interpreted_header.version, MAX_SUPPORTED_FILE_VERSION ))); } // get byte offset to the flag let head = (interpreted_header.boolean_value_offset + flag_index) as usize; if head >= interpreted_header.file_size as usize { return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!( "Flag value offset goes beyond the end of the file." ))); } buf[head] = u8::from(flag_value).to_le_bytes()[0]; Ok(head) } #[cfg(test)] mod tests { use super::*; use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION}; #[test] // this test point locks down flag value update fn test_boolean_flag_value_update() { let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); let value_offset = flag_value_list.header.boolean_value_offset; let mut content = flag_value_list.into_bytes(); let true_byte = u8::from(true).to_le_bytes()[0]; let false_byte = u8::from(false).to_le_bytes()[0]; for i in 0..flag_value_list.header.num_flags { let offset = (value_offset + i) as usize; update_boolean_flag_value(&mut content, i, true).unwrap(); assert_eq!(content[offset], true_byte); update_boolean_flag_value(&mut content, i, false).unwrap(); assert_eq!(content[offset], false_byte); } } #[test] // this test point locks down update beyond the end of boolean section fn test_boolean_out_of_range() { let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err(); assert_eq!( format!("{:?}", error), "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)" ); } #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let mut flag_value = value_list.into_bytes(); let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err(); assert_eq!( format!("{:?}", error), format!( "HigherStorageFileVersion(Cannot write to storage file with a higher version of {} with lib version {})", MAX_SUPPORTED_FILE_VERSION + 1, MAX_SUPPORTED_FILE_VERSION ) ); } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/src/lib.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aconfig_storage_write_api` is a crate that defines write apis to update flag value //! in storage file. It provides one api to interface with storage files. pub mod flag_info_update; pub mod flag_value_update; pub mod mapped_file; #[cfg(test)] mod test_utils; use aconfig_storage_file::{AconfigStorageError, FlagValueType}; use anyhow::anyhow; use memmap2::MmapMut; /// Get read write mapped storage files. /// /// \input file_path: path to the storage file /// /// # Safety /// /// The memory mapped file may have undefined behavior if there are writes to this /// file not thru this memory mapped file or there are concurrent writes to this /// memory mapped file. Ensure all writes to the underlying file are thru this memory /// mapped file and there are no concurrent writes. pub unsafe fn map_mutable_storage_file(file_path: &str) -> Result { crate::mapped_file::map_file(file_path) } /// Set boolean flag value thru mapped file and flush the change to file /// /// \input mapped_file: the mapped flag value file /// \input index: flag index /// \input value: updated flag value /// \return a result of () /// pub fn set_boolean_flag_value( file: &mut MmapMut, index: u32, value: bool, ) -> Result<(), AconfigStorageError> { crate::flag_value_update::update_boolean_flag_value(file, index, value)?; file.flush().map_err(|errmsg| { AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg)) }) } /// Set if flag is has server override thru mapped file and flush the change to file /// /// \input mapped_file: the mapped flag info file /// \input index: flag index /// \input value: updated flag has server override value /// \return a result of () /// pub fn set_flag_has_server_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError> { crate::flag_info_update::update_flag_has_server_override(file, flag_type, index, value)?; file.flush().map_err(|errmsg| { AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg)) }) } /// Set if flag has local override thru mapped file and flush the change to file /// /// \input mapped_file: the mapped flag info file /// \input index: flag index /// \input value: updated flag has local override value /// \return a result of () /// pub fn set_flag_has_local_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError> { crate::flag_info_update::update_flag_has_local_override(file, flag_type, index, value)?; file.flush().map_err(|errmsg| { AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg)) }) } // *************************************** // // CC INTERLOP // *************************************** // // Exported rust data structure and methods, c++ code will be generated #[cxx::bridge] mod ffi { // Flag value update return for cc interlop pub struct BooleanFlagValueUpdateCXX { pub update_success: bool, pub offset: usize, pub error_message: String, } // Flag has server override update return for cc interlop pub struct FlagHasServerOverrideUpdateCXX { pub update_success: bool, pub offset: usize, pub error_message: String, } // Flag has local override update return for cc interlop pub struct FlagHasLocalOverrideUpdateCXX { pub update_success: bool, pub offset: usize, pub error_message: String, } // Rust export to c++ extern "Rust" { pub fn update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> BooleanFlagValueUpdateCXX; pub fn update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasServerOverrideUpdateCXX; pub fn update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasLocalOverrideUpdateCXX; } } pub(crate) fn update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> ffi::BooleanFlagValueUpdateCXX { match crate::flag_value_update::update_boolean_flag_value(file, offset, value) { Ok(head) => ffi::BooleanFlagValueUpdateCXX { update_success: true, offset: head, error_message: String::from(""), }, Err(errmsg) => ffi::BooleanFlagValueUpdateCXX { update_success: false, offset: usize::MAX, error_message: format!("{:?}", errmsg), }, } } pub(crate) fn update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasServerOverrideUpdateCXX { match FlagValueType::try_from(flag_type) { Ok(value_type) => { match crate::flag_info_update::update_flag_has_server_override( file, value_type, offset, value, ) { Ok(head) => ffi::FlagHasServerOverrideUpdateCXX { update_success: true, offset: head, error_message: String::from(""), }, Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX { update_success: false, offset: usize::MAX, error_message: format!("{:?}", errmsg), }, } } Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX { update_success: false, offset: usize::MAX, error_message: format!("{:?}", errmsg), }, } } pub(crate) fn update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasLocalOverrideUpdateCXX { match FlagValueType::try_from(flag_type) { Ok(value_type) => { match crate::flag_info_update::update_flag_has_local_override( file, value_type, offset, value, ) { Ok(head) => ffi::FlagHasLocalOverrideUpdateCXX { update_success: true, offset: head, error_message: String::from(""), }, Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX { update_success: false, offset: usize::MAX, error_message: format!("{:?}", errmsg), }, } } Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX { update_success: false, offset: usize::MAX, error_message: format!("{:?}", errmsg), }, } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::copy_to_temp_file; use aconfig_storage_file::FlagInfoBit; use aconfig_storage_read_api::flag_info_query::find_flag_attribute; use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value; use std::fs::File; use std::io::Read; fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool { let mut f = File::open(&file).unwrap(); let mut bytes = Vec::new(); f.read_to_end(&mut bytes).unwrap(); find_boolean_flag_value(&bytes, offset).unwrap() } #[test] fn test_set_boolean_flag_value() { let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap(); let flag_value_path = flag_value_file.path().display().to_string(); // SAFETY: // The safety here is guaranteed as only this single threaded test process will // write to this file unsafe { let mut file = map_mutable_storage_file(&flag_value_path).unwrap(); for i in 0..8 { set_boolean_flag_value(&mut file, i, true).unwrap(); let value = get_boolean_flag_value_at_offset(&flag_value_path, i); assert_eq!(value, true); set_boolean_flag_value(&mut file, i, false).unwrap(); let value = get_boolean_flag_value_at_offset(&flag_value_path, i); assert_eq!(value, false); } } } fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 { let mut f = File::open(&file).unwrap(); let mut bytes = Vec::new(); f.read_to_end(&mut bytes).unwrap(); find_flag_attribute(&bytes, value_type, offset).unwrap() } #[test] fn test_set_flag_has_server_override() { let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap(); let flag_info_path = flag_info_file.path().display().to_string(); // SAFETY: // The safety here is guaranteed as only this single threaded test process will // write to this file unsafe { let mut file = map_mutable_storage_file(&flag_info_path).unwrap(); for i in 0..8 { set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0); set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0); } } } #[test] fn test_set_flag_has_local_override() { let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap(); let flag_info_path = flag_info_file.path().display().to_string(); // SAFETY: // The safety here is guaranteed as only this single threaded test process will // write to this file unsafe { let mut file = map_mutable_storage_file(&flag_info_path).unwrap(); for i in 0..8 { set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0); set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0); } } } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::anyhow; use memmap2::MmapMut; use std::fs::{self, OpenOptions}; use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail}; /// Get the mutable memory mapping of a storage file /// /// # Safety /// /// The memory mapped file may have undefined behavior if there are writes to this /// file not thru this memory mapped file or there are concurrent writes to this /// memory mapped file. Ensure all writes to the underlying file are thru this memory /// mapped file and there are no concurrent writes. pub(crate) unsafe fn map_file(file_path: &str) -> Result { // make sure file has read write permission let perms = fs::metadata(file_path).unwrap().permissions(); if perms.readonly() { return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path))); } let file = OpenOptions::new().read(true).write(true).open(file_path).map_err(|errmsg| { FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)) })?; unsafe { let mapped_file = MmapMut::map_mut(&file).map_err(|errmsg| { MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg)) })?; Ok(mapped_file) } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::copy_to_temp_file; use std::io::Read; #[test] fn test_mapped_file_contents() { let mut rw_val_file = copy_to_temp_file("./tests/flag.val", false).unwrap(); let mut rw_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap(); let flag_val = rw_val_file.path().display().to_string(); let flag_info = rw_info_file.path().display().to_string(); let mut content = Vec::new(); rw_val_file.read_to_end(&mut content).unwrap(); // SAFETY: // The safety here is guaranteed here as no writes happens to this temp file unsafe { let mmaped_file = map_file(&flag_val).unwrap(); assert_eq!(mmaped_file[..], content[..]); } let mut content = Vec::new(); rw_info_file.read_to_end(&mut content).unwrap(); // SAFETY: // The safety here is guaranteed here as no writes happens to this temp file unsafe { let mmaped_file = map_file(&flag_info).unwrap(); assert_eq!(mmaped_file[..], content[..]); } } #[test] fn test_mapped_read_only_file() { let ro_val_file = copy_to_temp_file("./tests/flag.val", true).unwrap(); let flag_val = ro_val_file.path().display().to_string(); // SAFETY: // The safety here is guaranteed here as no writes happens to this temp file unsafe { let error = map_file(&flag_val).unwrap_err(); assert_eq!( format!("{:?}", error), format!("MapFileFail(fail to map non read write storage file {})", flag_val) ); } } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/src/test_utils.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use anyhow::Result; use std::fs; use tempfile::NamedTempFile; /// Create temp file copy pub(crate) fn copy_to_temp_file(source_file: &str, read_only: bool) -> Result { let file = NamedTempFile::new()?; fs::copy(source_file, file.path())?; if read_only { let file_name = file.path().display().to_string(); let mut perms = fs::metadata(file_name).unwrap().permissions(); perms.set_readonly(true); fs::set_permissions(file.path(), perms.clone()).unwrap(); } Ok(file) } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/tests/Android.bp ================================================ rust_test { name: "aconfig_storage_write_api.test.rust", srcs: [ "storage_write_api_test.rs", ], rustlibs: [ "libanyhow", "libaconfig_storage_file", "libaconfig_storage_read_api", "libaconfig_storage_write_api", "libprotobuf", "libtempfile", ], data: [ "flag.val", "flag.info", ], test_suites: ["general-tests"], } cc_test { name: "aconfig_storage_write_api.test.cpp", srcs: [ "storage_write_api_test.cpp", ], static_libs: [ "libgmock", "libaconfig_storage_read_api_cc", "libaconfig_storage_write_api_cc", "libbase", "liblog", ], data: [ "flag.val", "flag.info", ], test_suites: [ "device-tests", "general-tests", ], generated_headers: [ "cxx-bridge-header", "libcxx_aconfig_storage_read_api_bridge_header", ], generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"], whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"], } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "aconfig_storage/aconfig_storage_read_api.hpp" #include "aconfig_storage/aconfig_storage_write_api.hpp" #include #include #include #include "rust/cxx.h" #include "aconfig_storage/lib.rs.h" using namespace android::base; namespace api = aconfig_storage; namespace private_api = aconfig_storage::private_internal_api; class AconfigStorageTest : public ::testing::Test { protected: Result copy_to_rw_temp_file(std::string const& source_file) { auto temp_file = std::string(std::tmpnam(nullptr)); auto content = std::string(); if (!ReadFileToString(source_file, &content)) { return Error() << "failed to read file: " << source_file; } if (!WriteStringToFile(content, temp_file)) { return Error() << "failed to copy file: " << source_file; } if (chmod(temp_file.c_str(), S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) == -1) { return Error() << "failed to chmod"; } return temp_file; } void SetUp() override { auto const test_dir = android::base::GetExecutableDirectory(); flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val"); flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info"); } void TearDown() override { std::remove(flag_val.c_str()); std::remove(flag_info.c_str()); } std::string flag_val; std::string flag_info; }; /// Negative test to lock down the error when mapping a non writeable storage file TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) { ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1); auto mapped_file_result = api::map_mutable_storage_file(flag_val); ASSERT_FALSE(mapped_file_result.ok()); auto it = mapped_file_result.error().message().find("cannot map nonwriteable file"); ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message(); } /// Test to lock down storage flag value update api TEST_F(AconfigStorageTest, test_boolean_flag_value_update) { auto mapped_file_result = api::map_mutable_storage_file(flag_val); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); for (int offset = 0; offset < 8; ++offset) { auto update_result = api::set_boolean_flag_value(*mapped_file, offset, true); ASSERT_TRUE(update_result.ok()); auto value = api::get_boolean_flag_value(*mapped_file, offset); ASSERT_TRUE(value.ok()); ASSERT_TRUE(*value); } // load the file on disk and check has been updated std::ifstream file(flag_val, std::ios::binary | std::ios::ate); std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(size); file.read(reinterpret_cast(buffer.data()), size); auto content = rust::Slice( buffer.data(), mapped_file->file_size); for (int offset = 0; offset < 8; ++offset) { auto value_cxx = get_boolean_flag_value_cxx(content, offset); ASSERT_TRUE(value_cxx.query_success); ASSERT_TRUE(value_cxx.flag_value); } } /// Negative test to lock down the error when querying flag value out of range TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) { auto mapped_file_result = api::map_mutable_storage_file(flag_val); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); auto update_result = api::set_boolean_flag_value(*mapped_file, 8, true); ASSERT_FALSE(update_result.ok()); ASSERT_EQ(update_result.error().message(), std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)")); } /// Test to lock down storage flag has server override update api TEST_F(AconfigStorageTest, test_flag_has_server_override_update) { auto mapped_file_result = api::map_mutable_storage_file(flag_info); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); for (int offset = 0; offset < 8; ++offset) { auto update_result = api::set_flag_has_server_override( *mapped_file, api::FlagValueType::Boolean, offset, true); ASSERT_TRUE(update_result.ok()) << update_result.error(); auto attribute = api::get_flag_attribute( *mapped_file, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.ok()); ASSERT_TRUE(*attribute & api::FlagInfoBit::HasServerOverride); } // load the file on disk and check has been updated std::ifstream file(flag_info, std::ios::binary | std::ios::ate); std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(size); file.read(reinterpret_cast(buffer.data()), size); auto content = rust::Slice( buffer.data(), mapped_file->file_size); for (int offset = 0; offset < 8; ++offset) { auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.query_success); ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride); } for (int offset = 0; offset < 8; ++offset) { auto update_result = api::set_flag_has_server_override( *mapped_file, api::FlagValueType::Boolean, offset, false); ASSERT_TRUE(update_result.ok()); auto attribute = api::get_flag_attribute( *mapped_file, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.ok()); ASSERT_FALSE(*attribute & api::FlagInfoBit::HasServerOverride); } std::ifstream file2(flag_info, std::ios::binary); buffer.clear(); file2.read(reinterpret_cast(buffer.data()), size); for (int offset = 0; offset < 8; ++offset) { auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.query_success); ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride); } } /// Test to lock down storage flag has local override update api TEST_F(AconfigStorageTest, test_flag_has_local_override_update) { auto mapped_file_result = api::map_mutable_storage_file(flag_info); ASSERT_TRUE(mapped_file_result.ok()); auto mapped_file = std::unique_ptr(*mapped_file_result); for (int offset = 0; offset < 8; ++offset) { auto update_result = api::set_flag_has_local_override( *mapped_file, api::FlagValueType::Boolean, offset, true); ASSERT_TRUE(update_result.ok()); auto attribute = api::get_flag_attribute( *mapped_file, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.ok()); ASSERT_TRUE(*attribute & api::FlagInfoBit::HasLocalOverride); } // load the file on disk and check has been updated std::ifstream file(flag_info, std::ios::binary | std::ios::ate); std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(size); file.read(reinterpret_cast(buffer.data()), size); auto content = rust::Slice( buffer.data(), mapped_file->file_size); for (int offset = 0; offset < 8; ++offset) { auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.query_success); ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride); } for (int offset = 0; offset < 8; ++offset) { auto update_result = api::set_flag_has_local_override( *mapped_file, api::FlagValueType::Boolean, offset, false); ASSERT_TRUE(update_result.ok()); auto attribute = api::get_flag_attribute( *mapped_file, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.ok()); ASSERT_FALSE(*attribute & api::FlagInfoBit::HasLocalOverride); } std::ifstream file2(flag_info, std::ios::binary); buffer.clear(); file2.read(reinterpret_cast(buffer.data()), size); for (int offset = 0; offset < 8; ++offset) { auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset); ASSERT_TRUE(attribute.query_success); ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride); } } ================================================ FILE: tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs ================================================ #[cfg(not(feature = "cargo"))] mod aconfig_storage_write_api_test { use aconfig_storage_file::{FlagInfoBit, FlagValueType}; use aconfig_storage_read_api::flag_info_query::find_flag_attribute; use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value; use aconfig_storage_write_api::{ map_mutable_storage_file, set_boolean_flag_value, set_flag_has_local_override, set_flag_has_server_override, }; use std::fs::{self, File}; use std::io::Read; use tempfile::NamedTempFile; /// Create temp file copy fn copy_to_temp_rw_file(source_file: &str) -> NamedTempFile { let file = NamedTempFile::new().unwrap(); fs::copy(source_file, file.path()).unwrap(); file } /// Get boolean flag value from offset fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool { let mut f = File::open(file).unwrap(); let mut bytes = Vec::new(); f.read_to_end(&mut bytes).unwrap(); find_boolean_flag_value(&bytes, offset).unwrap() } /// Get flag attribute at offset fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 { let mut f = File::open(file).unwrap(); let mut bytes = Vec::new(); f.read_to_end(&mut bytes).unwrap(); find_flag_attribute(&bytes, value_type, offset).unwrap() } #[test] /// Test to lock down flag value update api fn test_boolean_flag_value_update() { let flag_value_file = copy_to_temp_rw_file("./flag.val"); let flag_value_path = flag_value_file.path().display().to_string(); // SAFETY: // The safety here is ensured as only this single threaded test process will // write to this file let mut file = unsafe { map_mutable_storage_file(&flag_value_path).unwrap() }; for i in 0..8 { set_boolean_flag_value(&mut file, i, true).unwrap(); let value = get_boolean_flag_value_at_offset(&flag_value_path, i); assert!(value); set_boolean_flag_value(&mut file, i, false).unwrap(); let value = get_boolean_flag_value_at_offset(&flag_value_path, i); assert!(!value); } } #[test] /// Test to lock down flag has server override update api fn test_set_flag_has_server_override() { let flag_info_file = copy_to_temp_rw_file("./flag.info"); let flag_info_path = flag_info_file.path().display().to_string(); // SAFETY: // The safety here is ensured as only this single threaded test process will // write to this file let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() }; for i in 0..8 { set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0); set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0); } } #[test] /// Test to lock down flag has local override update api fn test_set_flag_has_local_override() { let flag_info_file = copy_to_temp_rw_file("./flag.info"); let flag_info_path = flag_info_file.path().display().to_string(); // SAFETY: // The safety here is ensured as only this single threaded test process will // write to this file let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() }; for i in 0..8 { set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0); set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap(); let attribute = get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i); assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0); } } } ================================================ FILE: tools/aconfig/aflags/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "aflags.defaults", edition: "2021", clippy_lints: "android", lints: "android", srcs: ["src/main.rs"], rustlibs: [ "libaconfig_device_paths", "libaconfig_flags", "libaconfig_protos", "libaconfigd_protos_rust", "libaconfig_storage_read_api", "libaconfig_storage_file", "libanyhow", "libclap", "libnix", "libprotobuf", "libregex", // TODO: b/371021174 remove this fake dependency once we find a proper strategy to // deal with test aconfig libs are not present in storage because they are never used // by the actual build "libaconfig_test_rust_library", ], } rust_binary { name: "aflags", host_supported: true, defaults: ["aflags.defaults"], apex_available: [ "//apex_available:platform", ], } rust_test_host { name: "aflags.test", defaults: ["aflags.defaults"], test_suites: ["general-tests"], } ================================================ FILE: tools/aconfig/aflags/Cargo.toml ================================================ [package] name = "aflags" version = "0.1.0" edition = "2021" [dependencies] anyhow = "1.0.69" paste = "1.0.11" protobuf = "3.2.0" regex = "1.10.3" aconfig_protos = { path = "../aconfig_protos" } aconfigd_protos = { version = "0.1.0", path = "../../../../../packages/modules/ConfigInfrastructure/aconfigd/proto"} nix = { version = "0.28.0", features = ["user"] } aconfig_storage_file = { version = "0.1.0", path = "../aconfig_storage_file" } aconfig_storage_read_api = { version = "0.1.0", path = "../aconfig_storage_read_api" } clap = {version = "4.5.2" } aconfig_device_paths = { version = "0.1.0", path = "../aconfig_device_paths" } aconfig_flags = { version = "0.1.0", path = "../aconfig_flags" } ================================================ FILE: tools/aconfig/aflags/src/aconfig_storage_source.rs ================================================ use crate::load_protos; use crate::{Flag, FlagSource}; use crate::{FlagPermission, FlagValue, ValuePickedFrom}; use aconfigd_protos::{ ProtoFlagQueryReturnMessage, ProtoListStorageMessage, ProtoListStorageMessageMsg, ProtoStorageRequestMessage, ProtoStorageRequestMessageMsg, ProtoStorageRequestMessages, ProtoStorageReturnMessage, ProtoStorageReturnMessageMsg, ProtoStorageReturnMessages, }; use anyhow::anyhow; use anyhow::Result; use protobuf::Message; use protobuf::SpecialFields; use std::collections::HashMap; use std::io::{Read, Write}; use std::net::Shutdown; use std::os::unix::net::UnixStream; pub struct AconfigStorageSource {} static ACONFIGD_SYSTEM_SOCKET_NAME: &str = "/dev/socket/aconfigd_system"; static ACONFIGD_MAINLINE_SOCKET_NAME: &str = "/dev/socket/aconfigd_mainline"; enum AconfigdSocket { System, Mainline, } impl AconfigdSocket { pub fn name(&self) -> &str { match self { AconfigdSocket::System => ACONFIGD_SYSTEM_SOCKET_NAME, AconfigdSocket::Mainline => ACONFIGD_MAINLINE_SOCKET_NAME, } } } fn load_flag_to_container() -> Result> { Ok(load_protos::load()?.into_iter().map(|p| (p.qualified_name(), p.container)).collect()) } fn convert(msg: ProtoFlagQueryReturnMessage, containers: &HashMap) -> Result { let (value, value_picked_from) = match ( &msg.boot_flag_value, msg.default_flag_value, msg.local_flag_value, msg.has_local_override, ) { (_, _, Some(local), Some(has_local)) if has_local => { (FlagValue::try_from(local.as_str())?, ValuePickedFrom::Local) } (Some(boot), Some(default), _, _) => { let value = FlagValue::try_from(boot.as_str())?; if *boot == default { (value, ValuePickedFrom::Default) } else { (value, ValuePickedFrom::Server) } } _ => return Err(anyhow!("missing override")), }; let staged_value = match (msg.boot_flag_value, msg.server_flag_value, msg.has_server_override) { (Some(boot), Some(server), _) if boot == server => None, (Some(boot), Some(server), Some(has_server)) if boot != server && has_server => { Some(FlagValue::try_from(server.as_str())?) } _ => None, }; let permission = match msg.is_readwrite { Some(is_readwrite) => { if is_readwrite { FlagPermission::ReadWrite } else { FlagPermission::ReadOnly } } None => return Err(anyhow!("missing permission")), }; let name = msg.flag_name.ok_or(anyhow!("missing flag name"))?; let package = msg.package_name.ok_or(anyhow!("missing package name"))?; let qualified_name = format!("{package}.{name}"); Ok(Flag { name, package, value, permission, value_picked_from, staged_value, container: containers .get(&qualified_name) .cloned() .unwrap_or_else(|| "".to_string()) .to_string(), // TODO: remove once DeviceConfig is not in the CLI. namespace: "-".to_string(), }) } fn read_from_socket(socket: AconfigdSocket) -> Result> { let messages = ProtoStorageRequestMessages { msgs: vec![ProtoStorageRequestMessage { msg: Some(ProtoStorageRequestMessageMsg::ListStorageMessage(ProtoListStorageMessage { msg: Some(ProtoListStorageMessageMsg::All(true)), special_fields: SpecialFields::new(), })), special_fields: SpecialFields::new(), }], special_fields: SpecialFields::new(), }; let mut socket = UnixStream::connect(socket.name())?; let message_buffer = messages.write_to_bytes()?; let mut message_length_buffer: [u8; 4] = [0; 4]; let message_size = &message_buffer.len(); message_length_buffer[0] = (message_size >> 24) as u8; message_length_buffer[1] = (message_size >> 16) as u8; message_length_buffer[2] = (message_size >> 8) as u8; message_length_buffer[3] = *message_size as u8; socket.write_all(&message_length_buffer)?; socket.write_all(&message_buffer)?; socket.shutdown(Shutdown::Write)?; let mut response_length_buffer: [u8; 4] = [0; 4]; socket.read_exact(&mut response_length_buffer)?; let response_length = u32::from_be_bytes(response_length_buffer) as usize; let mut response_buffer = vec![0; response_length]; socket.read_exact(&mut response_buffer)?; let response: ProtoStorageReturnMessages = protobuf::Message::parse_from_bytes(&response_buffer)?; match response.msgs.as_slice() { [ProtoStorageReturnMessage { msg: Some(ProtoStorageReturnMessageMsg::ListStorageMessage(list_storage_message)), .. }] => Ok(list_storage_message.flags.clone()), _ => Err(anyhow!("unexpected response from aconfigd")), } } impl FlagSource for AconfigStorageSource { fn list_flags() -> Result> { let containers = load_flag_to_container()?; let system_messages = read_from_socket(AconfigdSocket::System); let mainline_messages = read_from_socket(AconfigdSocket::Mainline); let mut all_messages = vec![]; if let Ok(system_messages) = system_messages { all_messages.extend_from_slice(&system_messages); } if let Ok(mainline_messages) = mainline_messages { all_messages.extend_from_slice(&mainline_messages); } all_messages .into_iter() .map(|query_message| convert(query_message.clone(), &containers)) .collect() } fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> { todo!() } } ================================================ FILE: tools/aconfig/aflags/src/device_config_source.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::load_protos; use crate::{Flag, FlagSource, FlagValue, ValuePickedFrom}; use anyhow::{anyhow, bail, Result}; use regex::Regex; use std::collections::HashMap; use std::process::Command; use std::str; pub struct DeviceConfigSource {} fn parse_device_config(raw: &str) -> Result> { let mut flags = HashMap::new(); let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$")?; for capture in regex.captures_iter(raw) { let key = capture.get(1).ok_or(anyhow!("invalid device_config output"))?.as_str().to_string(); let value = FlagValue::try_from( capture.get(2).ok_or(anyhow!("invalid device_config output"))?.as_str(), )?; flags.insert(key, value); } Ok(flags) } fn read_device_config_output(command: &[&str]) -> Result { let output = Command::new("/system/bin/device_config").args(command).output()?; if !output.status.success() { let reason = match output.status.code() { Some(code) => { let output = str::from_utf8(&output.stdout)?; if !output.is_empty() { format!("exit code {code}, output was {output}") } else { format!("exit code {code}") } } None => "terminated by signal".to_string(), }; bail!("failed to access flag storage: {}", reason); } Ok(str::from_utf8(&output.stdout)?.to_string()) } fn read_device_config_flags() -> Result> { let list_output = read_device_config_output(&["list"])?; parse_device_config(&list_output) } /// Parse the list of newline-separated staged flags. /// /// The output is a newline-sepaarated list of entries which follow this format: /// `namespace*flagname=value` /// /// The resulting map maps from `namespace/flagname` to `value`, if a staged flag exists for /// `namespace/flagname`. fn parse_staged_flags(raw: &str) -> Result> { let mut flags = HashMap::new(); for line in raw.split('\n') { match (line.find('*'), line.find('=')) { (Some(star_index), Some(equal_index)) => { let namespace = &line[..star_index]; let flag = &line[star_index + 1..equal_index]; if let Ok(value) = FlagValue::try_from(&line[equal_index + 1..]) { flags.insert(namespace.to_owned() + "/" + flag, value); } } _ => continue, }; } Ok(flags) } fn read_staged_flags() -> Result> { let staged_flags_output = read_device_config_output(&["list", "staged"])?; parse_staged_flags(&staged_flags_output) } fn reconcile( pb_flags: &[Flag], dc_flags: HashMap, staged_flags: HashMap, ) -> Vec { pb_flags .iter() .map(|f| { let server_override = dc_flags.get(&format!("{}/{}", f.namespace, f.qualified_name())); let (value_picked_from, selected_value) = match server_override { Some(value) if *value != f.value => (ValuePickedFrom::Server, *value), _ => (ValuePickedFrom::Default, f.value), }; Flag { value_picked_from, value: selected_value, ..f.clone() } }) .map(|f| { let staged_value = staged_flags .get(&format!("{}/{}", f.namespace, f.qualified_name())) .map(|value| if *value != f.value { Some(*value) } else { None }) .unwrap_or(None); Flag { staged_value, ..f } }) .collect() } impl FlagSource for DeviceConfigSource { fn list_flags() -> Result> { let pb_flags = load_protos::load()?; let dc_flags = read_device_config_flags()?; let staged_flags = read_staged_flags()?; let flags = reconcile(&pb_flags, dc_flags, staged_flags); Ok(flags) } fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()> { read_device_config_output(&["put", namespace, qualified_name, value]).map(|_| ()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_device_config() { let input = r#" namespace_one/com.foo.bar.flag_one=true namespace_one/com.foo.bar.flag_two=false random_noise; namespace_two/android.flag_one=true namespace_two/android.flag_two=nonsense "#; let expected = HashMap::from([ ("namespace_one/com.foo.bar.flag_one".to_string(), FlagValue::Enabled), ("namespace_one/com.foo.bar.flag_two".to_string(), FlagValue::Disabled), ("namespace_two/android.flag_one".to_string(), FlagValue::Enabled), ]); let actual = parse_device_config(input).unwrap(); assert_eq!(expected, actual); } } ================================================ FILE: tools/aconfig/aflags/src/load_protos.rs ================================================ use crate::{Flag, FlagPermission, FlagValue, ValuePickedFrom}; use aconfig_protos::ProtoFlagPermission as ProtoPermission; use aconfig_protos::ProtoFlagState as ProtoState; use aconfig_protos::ProtoParsedFlag; use aconfig_protos::ProtoParsedFlags; use anyhow::Result; use std::fs; use std::path::Path; // TODO(b/329875578): use container field directly instead of inferring. fn infer_container(path: &Path) -> String { let path_str = path.to_string_lossy(); path_str .strip_prefix("/apex/") .or_else(|| path_str.strip_prefix('/')) .unwrap_or(&path_str) .strip_suffix("/etc/aconfig_flags.pb") .unwrap_or(&path_str) .to_string() } fn convert_parsed_flag(path: &Path, flag: &ProtoParsedFlag) -> Flag { let namespace = flag.namespace().to_string(); let package = flag.package().to_string(); let name = flag.name().to_string(); let value = match flag.state() { ProtoState::ENABLED => FlagValue::Enabled, ProtoState::DISABLED => FlagValue::Disabled, }; let permission = match flag.permission() { ProtoPermission::READ_ONLY => FlagPermission::ReadOnly, ProtoPermission::READ_WRITE => FlagPermission::ReadWrite, }; Flag { namespace, package, name, container: infer_container(path), value, staged_value: None, permission, value_picked_from: ValuePickedFrom::Default, } } pub(crate) fn load() -> Result> { let mut result = Vec::new(); let paths = aconfig_device_paths::parsed_flags_proto_paths()?; for path in paths { let Ok(bytes) = fs::read(&path) else { eprintln!("warning: failed to read {:?}", path); continue; }; let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?; for flag in parsed_flags.parsed_flag { // TODO(b/334954748): enforce one-container-per-flag invariant. result.push(convert_parsed_flag(&path, &flag)); } } Ok(result) } pub(crate) fn list_containers() -> Result> { Ok(aconfig_device_paths::parsed_flags_proto_paths()? .into_iter() .map(|p| infer_container(&p)) .collect()) } ================================================ FILE: tools/aconfig/aflags/src/main.rs ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `aflags` is a device binary to read and write aconfig flags. use std::env; use std::process::{Command as OsCommand, Stdio}; use anyhow::{anyhow, ensure, Result}; use clap::Parser; mod device_config_source; use device_config_source::DeviceConfigSource; mod aconfig_storage_source; use aconfig_storage_source::AconfigStorageSource; mod load_protos; #[derive(Clone, PartialEq, Debug)] enum FlagPermission { ReadOnly, ReadWrite, } impl std::fmt::Display for FlagPermission { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match &self { Self::ReadOnly => "read-only", Self::ReadWrite => "read-write", } ) } } #[derive(Clone, Debug)] enum ValuePickedFrom { Default, Server, Local, } impl std::fmt::Display for ValuePickedFrom { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match &self { Self::Default => "default", Self::Server => "server", Self::Local => "local", } ) } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum FlagValue { Enabled, Disabled, } impl TryFrom<&str> for FlagValue { type Error = anyhow::Error; fn try_from(value: &str) -> std::result::Result { match value { "true" | "enabled" => Ok(Self::Enabled), "false" | "disabled" => Ok(Self::Disabled), _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)), } } } impl std::fmt::Display for FlagValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match &self { Self::Enabled => "enabled", Self::Disabled => "disabled", } ) } } #[derive(Clone, Debug)] struct Flag { namespace: String, name: String, package: String, container: String, value: FlagValue, staged_value: Option, permission: FlagPermission, value_picked_from: ValuePickedFrom, } impl Flag { fn qualified_name(&self) -> String { format!("{}.{}", self.package, self.name) } fn display_staged_value(&self) -> String { match (&self.permission, self.staged_value) { (FlagPermission::ReadOnly, _) => "-".to_string(), (FlagPermission::ReadWrite, None) => "-".to_string(), (FlagPermission::ReadWrite, Some(v)) => format!("(->{})", v), } } } trait FlagSource { fn list_flags() -> Result>; fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>; } enum FlagSourceType { DeviceConfig, AconfigStorage, } const ABOUT_TEXT: &str = "Tool for reading and writing flags. Rows in the table from the `list` command follow this format: package flag_name value provenance permission container * `package`: package set for this flag in its .aconfig definition. * `flag_name`: flag name, also set in definition. * `value`: the value read from the flag. * `staged_value`: the value on next boot: + `-`: same as current value + `(->enabled) flipped to enabled on boot. + `(->disabled) flipped to disabled on boot. * `provenance`: one of: + `default`: the flag value comes from its build-time default. + `server`: the flag value comes from a server override. * `permission`: read-write or read-only. * `container`: the container for the flag, configured in its definition. "; #[derive(Parser, Debug)] #[clap(long_about=ABOUT_TEXT)] struct Cli { #[clap(subcommand)] command: Command, } #[derive(Parser, Debug)] enum Command { /// List all aconfig flags on this device. List { /// Optionally filter by container name. #[clap(short = 'c', long = "container")] container: Option, }, /// Enable an aconfig flag on this device, on the next boot. Enable { /// . qualified_name: String, }, /// Disable an aconfig flag on this device, on the next boot. Disable { /// . qualified_name: String, }, /// Display which flag storage backs aconfig flags. WhichBacking, } struct PaddingInfo { longest_flag_col: usize, longest_val_col: usize, longest_staged_val_col: usize, longest_value_picked_from_col: usize, longest_permission_col: usize, } struct Filter { container: Option, } impl Filter { fn apply(&self, flags: &[Flag]) -> Vec { flags .iter() .filter(|flag| match &self.container { Some(c) => flag.container == *c, None => true, }) .cloned() .collect() } } fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String { let full_name = flag.qualified_name(); let p0 = info.longest_flag_col + 1; let val = flag.value.to_string(); let p1 = info.longest_val_col + 1; let staged_val = flag.display_staged_value(); let p2 = info.longest_staged_val_col + 1; let value_picked_from = flag.value_picked_from.to_string(); let p3 = info.longest_value_picked_from_col + 1; let perm = flag.permission.to_string(); let p4 = info.longest_permission_col + 1; let container = &flag.container; format!( "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n" ) } fn set_flag(qualified_name: &str, value: &str) -> Result<()> { let flags_binding = DeviceConfigSource::list_flags()?; let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or( anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"), )?; ensure!(flag.permission == FlagPermission::ReadWrite, format!("could not write flag '{qualified_name}', it is read-only for the current release configuration.")); DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?; Ok(()) } fn list(source_type: FlagSourceType, container: Option) -> Result { let flags_unfiltered = match source_type { FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?, FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?, }; if let Some(ref c) = container { ensure!( load_protos::list_containers()?.contains(c), format!("container '{}' not found", &c) ); } let flags = (Filter { container }).apply(&flags_unfiltered); let padding_info = PaddingInfo { longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0), longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0), longest_staged_val_col: flags .iter() .map(|f| f.display_staged_value().len()) .max() .unwrap_or(0), longest_value_picked_from_col: flags .iter() .map(|f| f.value_picked_from.to_string().len()) .max() .unwrap_or(0), longest_permission_col: flags .iter() .map(|f| f.permission.to_string().len()) .max() .unwrap_or(0), }; let mut result = String::from(""); for flag in flags { let row = format_flag_row(&flag, &padding_info); result.push_str(&row); } Ok(result) } fn display_which_backing() -> String { if aconfig_flags::auto_generated::enable_only_new_storage() { "aconfig_storage".to_string() } else { "device_config".to_string() } } fn invoke_updatable_aflags() { let updatable_command = "/apex/com.android.configinfrastructure/bin/aflags_updatable"; let args: Vec = env::args().collect(); let command_args = if args.len() >= 2 { &args[1..] } else { &["--help".to_string()] }; let mut child = OsCommand::new(updatable_command); for arg in command_args { child.arg(arg); } let output = child .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("failed to execute child") .wait_with_output() .expect("failed to execute command"); let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); if !output_str.is_empty() { println!("{}", output_str); } } fn main() -> Result<()> { if aconfig_flags::auto_generated::invoke_updatable_aflags() { invoke_updatable_aflags(); return Ok(()); } ensure!(nix::unistd::Uid::current().is_root(), "must be root"); let cli = Cli::parse(); let output = match cli.command { Command::List { container } => { if aconfig_flags::auto_generated::enable_only_new_storage() { list(FlagSourceType::AconfigStorage, container) .map_err(|err| anyhow!("could not list flags: {err}")) .map(Some) } else { list(FlagSourceType::DeviceConfig, container).map(Some) } } Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None), Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None), Command::WhichBacking => Ok(Some(display_which_backing())), }; match output { Ok(Some(text)) => println!("{text}"), Ok(None) => (), Err(message) => println!("Error: {message}"), } Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_filter_container() { let flags = vec![ Flag { namespace: "namespace".to_string(), name: "test1".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "system".to_string(), }, Flag { namespace: "namespace".to_string(), name: "test2".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "not_system".to_string(), }, Flag { namespace: "namespace".to_string(), name: "test3".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "system".to_string(), }, ]; assert_eq!((Filter { container: Some("system".to_string()) }).apply(&flags).len(), 2); } #[test] fn test_filter_no_container() { let flags = vec![ Flag { namespace: "namespace".to_string(), name: "test1".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "system".to_string(), }, Flag { namespace: "namespace".to_string(), name: "test2".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "not_system".to_string(), }, Flag { namespace: "namespace".to_string(), name: "test3".to_string(), package: "package".to_string(), value: FlagValue::Disabled, staged_value: None, permission: FlagPermission::ReadWrite, value_picked_from: ValuePickedFrom::Default, container: "system".to_string(), }, ]; assert_eq!((Filter { container: None }).apply(&flags).len(), 3); } } ================================================ FILE: tools/aconfig/convert_finalized_flags/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "convert_finalized_flags.defaults", edition: "2021", clippy_lints: "android", lints: "android", rustlibs: [ "libanyhow", "libclap", "libitertools", "libprotobuf", "libserde", "libserde_json", "libtempfile", "libtinytemplate", ], } rust_library_host { name: "libconvert_finalized_flags", crate_name: "convert_finalized_flags", defaults: ["convert_finalized_flags.defaults"], srcs: [ "src/lib.rs", ], } rust_binary_host { name: "convert_finalized_flags", defaults: ["convert_finalized_flags.defaults"], srcs: ["src/main.rs"], rustlibs: [ "libconvert_finalized_flags", "libserde_json", ], } rust_test_host { name: "convert_finalized_flags.test", defaults: ["convert_finalized_flags.defaults"], test_suites: ["general-tests"], srcs: ["src/lib.rs"], } genrule { name: "finalized_flags_record.json", srcs: [ "//prebuilts/sdk:finalized-api-flags", ], tool_files: ["extended_flags_list_35.txt"], out: ["finalized_flags_record.json"], tools: ["convert_finalized_flags"], cmd: "args=\"\" && " + "for f in $(locations //prebuilts/sdk:finalized-api-flags); " + " do args=\"$$args --flag_file_path $$f\"; done && " + "$(location convert_finalized_flags) $$args --extended-flag-file-path $(location extended_flags_list_35.txt) > $(out)", } ================================================ FILE: tools/aconfig/convert_finalized_flags/Cargo.toml ================================================ [package] name = "convert_finalized_flags" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] anyhow = "1.0.69" clap = { version = "4.1.8", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" tempfile = "3.13.0" ================================================ FILE: tools/aconfig/convert_finalized_flags/extended_flags_list_35.txt ================================================ ================================================ FILE: tools/aconfig/convert_finalized_flags/src/lib.rs ================================================ /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! Functions to extract finalized flag information from //! /prebuilts/sdk/#/finalized-flags.txt. //! These functions are very specific to that file setup as well as the format //! of the files (just a list of the fully-qualified flag names). //! There are also some helper functions for local building using cargo. These //! functions are only invoked via cargo for quick local testing and will not //! be used during actual soong building. They are marked as such. use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fs; use std::io::{self, BufRead}; const SDK_INT_MULTIPLIER: u32 = 100_000; /// Just the fully qualified flag name (package_name.flag_name). #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct FinalizedFlag { /// Name of the flag. pub flag_name: String, /// Name of the package. pub package_name: String, } /// API level in which the flag was finalized. #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct ApiLevel(pub i32); /// API level of the extended flags file of version 35 pub const EXTENDED_FLAGS_35_APILEVEL: ApiLevel = ApiLevel(35); /// Contains all flags finalized for a given API level. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] pub struct FinalizedFlagMap(HashMap>); impl FinalizedFlagMap { /// Creates a new, empty instance. pub fn new() -> Self { Self(HashMap::new()) } /// Convenience method for is_empty on the underlying map. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Returns the API level in which the flag was finalized . pub fn get_finalized_level(&self, flag: &FinalizedFlag) -> Option { for (api_level, flags_for_level) in &self.0 { if flags_for_level.contains(flag) { return Some(*api_level); } } None } /// Insert the flag into the map for the given level if the flag is not /// present in the map already - for *any* level (not just the one given). pub fn insert_if_new(&mut self, level: ApiLevel, flag: FinalizedFlag) { if self.contains(&flag) { return; } self.0.entry(level).or_default().insert(flag); } fn contains(&self, flag: &FinalizedFlag) -> bool { self.0.values().any(|flags_set| flags_set.contains(flag)) } } #[allow(dead_code)] // TODO: b/378936061: Use with SDK_INT_FULL check. fn parse_full_version(version: String) -> Result { let (major, minor) = if let Some(decimal_index) = version.find('.') { (version[..decimal_index].parse::()?, version[decimal_index + 1..].parse::()?) } else { (version.parse::()?, 0) }; if major >= 21474 { return Err(anyhow!("Major version too large, must be less than 21474.")); } if minor >= SDK_INT_MULTIPLIER { return Err(anyhow!("Minor version too large, must be less than {}.", SDK_INT_MULTIPLIER)); } Ok(major * SDK_INT_MULTIPLIER + minor) } const EXTENDED_FLAGS_LIST_35: &str = "extended_flags_list_35.txt"; /// Converts a string to an int. Will parse to int even if the string is "X.0". /// Returns error for "X.1". fn str_to_api_level(numeric_string: &str) -> Result { let float_value = numeric_string.parse::()?; if float_value.fract() == 0.0 { Ok(ApiLevel(float_value as i32)) } else { Err(anyhow!("Numeric string is float, can't parse to int.")) } } /// For each file, extracts the qualified flag names into a FinalizedFlag, then /// enters them in a map at the API level corresponding to their directory. /// Ex: /prebuilts/sdk/35/finalized-flags.txt -> {36, [flag1, flag2]}. pub fn read_files_to_map_using_path(flag_files: Vec) -> Result { let mut data_map = FinalizedFlagMap::new(); for flag_file in flag_files { // Split /path/sdk//finalized-flags.txt -> ['/path/sdk', 'int.int', 'finalized-flags.txt']. let flag_file_split: Vec = flag_file.clone().rsplitn(3, '/').map(|s| s.to_string()).collect(); if &flag_file_split[0] != "finalized-flags.txt" { return Err(anyhow!("Provided incorrect file, must be finalized-flags.txt")); } let api_level_string = &flag_file_split[1]; // For now, skip any directory with full API level, e.g. "36.1". The // finalized flag files each contain all flags finalized *up to* that // level (including prior levels), so skipping intermediate levels means // the flags will be included at the next full number. // TODO: b/378936061 - Support full SDK version. // In the future, we should error if provided a non-numeric directory. let Ok(api_level) = str_to_api_level(api_level_string) else { continue; }; let file = fs::File::open(&flag_file)?; io::BufReader::new(file).lines().for_each(|flag| { let flag = flag.unwrap_or_else(|_| panic!("Failed to read line from file {}", flag_file)); let finalized_flag = build_finalized_flag(&flag) .unwrap_or_else(|_| panic!("cannot build finalized flag {}", flag)); data_map.insert_if_new(api_level, finalized_flag); }); } Ok(data_map) } /// Read the qualified flag names into a FinalizedFlag set pub fn read_extend_file_to_map_using_path(extened_file: String) -> Result> { let (_, file_name) = extened_file.rsplit_once('/').ok_or(anyhow!("Invalid file: '{}'", extened_file))?; if file_name != EXTENDED_FLAGS_LIST_35 { return Err(anyhow!("Provided incorrect file, must be {}", EXTENDED_FLAGS_LIST_35)); } let file = fs::File::open(extened_file)?; let extended_flags = io::BufReader::new(file) .lines() .map(|flag| { let flag = flag.expect("Failed to read line from extended file"); build_finalized_flag(&flag) .unwrap_or_else(|_| panic!("cannot build finalized flag {}", flag)) }) .collect::>(); Ok(extended_flags) } fn build_finalized_flag(qualified_flag_name: &String) -> Result { // Split the qualified flag name into package and flag name: // com.my.package.name.my_flag_name -> ('com.my.package.name', 'my_flag_name') let (package_name, flag_name) = qualified_flag_name .rsplit_once('.') .ok_or(anyhow!("Invalid qualified flag name format: '{}'", qualified_flag_name))?; Ok(FinalizedFlag { flag_name: flag_name.to_string(), package_name: package_name.to_string() }) } #[cfg(test)] mod tests { use super::*; use std::fs::File; use std::io::Write; use tempfile::tempdir; const FLAG_FILE_NAME: &str = "finalized-flags.txt"; // Creates some flags for testing. fn create_test_flags() -> Vec { vec![ FinalizedFlag { flag_name: "name1".to_string(), package_name: "package1".to_string() }, FinalizedFlag { flag_name: "name2".to_string(), package_name: "package2".to_string() }, FinalizedFlag { flag_name: "name3".to_string(), package_name: "package3".to_string() }, ] } // Writes the fully qualified flag names in the given file. fn add_flags_to_file(flag_file: &mut File, flags: &[FinalizedFlag]) { for flag in flags { let _unused = writeln!(flag_file, "{}.{}", flag.package_name, flag.flag_name); } } #[test] fn test_read_flags_one_file() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt. let temp_dir = tempdir().unwrap(); let mut file_path = temp_dir.path().to_path_buf(); file_path.push("35"); fs::create_dir_all(&file_path).unwrap(); file_path.push(FLAG_FILE_NAME); let mut file = File::create(&file_path).unwrap(); // Write all flags to the file. add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]); let flag_file_path = file_path.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![flag_file_path]).unwrap(); assert_eq!(map.0.len(), 1); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0])); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[1])); } #[test] fn test_read_flags_two_files() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt and for 36. let temp_dir = tempdir().unwrap(); let mut file_path1 = temp_dir.path().to_path_buf(); file_path1.push("35"); fs::create_dir_all(&file_path1).unwrap(); file_path1.push(FLAG_FILE_NAME); let mut file1 = File::create(&file_path1).unwrap(); let mut file_path2 = temp_dir.path().to_path_buf(); file_path2.push("36"); fs::create_dir_all(&file_path2).unwrap(); file_path2.push(FLAG_FILE_NAME); let mut file2 = File::create(&file_path2).unwrap(); // Write all flags to the files. add_flags_to_file(&mut file1, &[flags[0].clone()]); add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]); let flag_file_path1 = file_path1.to_string_lossy().to_string(); let flag_file_path2 = file_path2.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap(); // Assert there are two API levels, 35 and 36. assert_eq!(map.0.len(), 2); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0])); // 36 should not have the first flag in the set, as it was finalized in // an earlier API level. assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1])); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2])); } #[test] fn test_read_flags_full_numbers() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt and for 36. let temp_dir = tempdir().unwrap(); let mut file_path1 = temp_dir.path().to_path_buf(); file_path1.push("35.0"); fs::create_dir_all(&file_path1).unwrap(); file_path1.push(FLAG_FILE_NAME); let mut file1 = File::create(&file_path1).unwrap(); let mut file_path2 = temp_dir.path().to_path_buf(); file_path2.push("36.0"); fs::create_dir_all(&file_path2).unwrap(); file_path2.push(FLAG_FILE_NAME); let mut file2 = File::create(&file_path2).unwrap(); // Write all flags to the files. add_flags_to_file(&mut file1, &[flags[0].clone()]); add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]); let flag_file_path1 = file_path1.to_string_lossy().to_string(); let flag_file_path2 = file_path2.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap(); assert_eq!(map.0.len(), 2); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0])); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1])); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2])); } #[test] fn test_read_flags_fractions_round_up() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt and for 36. let temp_dir = tempdir().unwrap(); let mut file_path1 = temp_dir.path().to_path_buf(); file_path1.push("35.1"); fs::create_dir_all(&file_path1).unwrap(); file_path1.push(FLAG_FILE_NAME); let mut file1 = File::create(&file_path1).unwrap(); let mut file_path2 = temp_dir.path().to_path_buf(); file_path2.push("36.0"); fs::create_dir_all(&file_path2).unwrap(); file_path2.push(FLAG_FILE_NAME); let mut file2 = File::create(&file_path2).unwrap(); // Write all flags to the files. add_flags_to_file(&mut file1, &[flags[0].clone()]); add_flags_to_file(&mut file2, &[flags[0].clone(), flags[1].clone(), flags[2].clone()]); let flag_file_path1 = file_path1.to_string_lossy().to_string(); let flag_file_path2 = file_path2.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![flag_file_path1, flag_file_path2]).unwrap(); // No flags were added in 35. All 35.1 flags were rolled up to 36. assert_eq!(map.0.len(), 1); assert!(!map.0.contains_key(&ApiLevel(35))); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[0])); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[1])); assert!(map.0.get(&ApiLevel(36)).unwrap().contains(&flags[2])); } #[test] fn test_read_flags_non_numeric() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt. let temp_dir = tempdir().unwrap(); let mut file_path = temp_dir.path().to_path_buf(); file_path.push("35"); fs::create_dir_all(&file_path).unwrap(); file_path.push(FLAG_FILE_NAME); let mut flag_file = File::create(&file_path).unwrap(); let mut invalid_path = temp_dir.path().to_path_buf(); invalid_path.push("sdk-annotations"); fs::create_dir_all(&invalid_path).unwrap(); invalid_path.push(FLAG_FILE_NAME); File::create(&invalid_path).unwrap(); // Write all flags to the file. add_flags_to_file(&mut flag_file, &[flags[0].clone(), flags[1].clone()]); let flag_file_path = file_path.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![ flag_file_path, invalid_path.to_string_lossy().to_string(), ]) .unwrap(); // No set should be created for sdk-annotations. assert_eq!(map.0.len(), 1); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[0])); assert!(map.0.get(&ApiLevel(35)).unwrap().contains(&flags[1])); } #[test] fn test_read_flags_wrong_file_err() { let flags = create_test_flags(); // Create the file /35/finalized-flags.txt. let temp_dir = tempdir().unwrap(); let mut file_path = temp_dir.path().to_path_buf(); file_path.push("35"); fs::create_dir_all(&file_path).unwrap(); file_path.push(FLAG_FILE_NAME); let mut flag_file = File::create(&file_path).unwrap(); let mut pre_flag_path = temp_dir.path().to_path_buf(); pre_flag_path.push("18"); fs::create_dir_all(&pre_flag_path).unwrap(); pre_flag_path.push("some_random_file.txt"); File::create(&pre_flag_path).unwrap(); // Write all flags to the file. add_flags_to_file(&mut flag_file, &[flags[0].clone(), flags[1].clone()]); let flag_file_path = file_path.to_string_lossy().to_string(); // Convert to map. let map = read_files_to_map_using_path(vec![ flag_file_path, pre_flag_path.to_string_lossy().to_string(), ]); assert!(map.is_err()); } #[test] fn test_flags_map_insert_if_new() { let flags = create_test_flags(); let mut map = FinalizedFlagMap::new(); let l35 = ApiLevel(35); let l36 = ApiLevel(36); map.insert_if_new(l35, flags[0].clone()); map.insert_if_new(l35, flags[1].clone()); map.insert_if_new(l35, flags[2].clone()); map.insert_if_new(l36, flags[0].clone()); assert!(map.0.get(&l35).unwrap().contains(&flags[0])); assert!(map.0.get(&l35).unwrap().contains(&flags[1])); assert!(map.0.get(&l35).unwrap().contains(&flags[2])); assert!(!map.0.contains_key(&l36)); } #[test] fn test_flags_map_get_level() { let flags = create_test_flags(); let mut map = FinalizedFlagMap::new(); let l35 = ApiLevel(35); let l36 = ApiLevel(36); map.insert_if_new(l35, flags[0].clone()); map.insert_if_new(l36, flags[1].clone()); assert_eq!(map.get_finalized_level(&flags[0]).unwrap(), l35); assert_eq!(map.get_finalized_level(&flags[1]).unwrap(), l36); } #[test] fn test_read_flag_from_extended_file() { let flags = create_test_flags(); // Create the file /35/extended_flags_list_35.txt let temp_dir = tempdir().unwrap(); let mut file_path = temp_dir.path().to_path_buf(); file_path.push("35"); fs::create_dir_all(&file_path).unwrap(); file_path.push(EXTENDED_FLAGS_LIST_35); let mut file = File::create(&file_path).unwrap(); // Write all flags to the file. add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]); let flags_set = read_extend_file_to_map_using_path(file_path.to_string_lossy().to_string()).unwrap(); assert_eq!(flags_set.len(), 2); assert!(flags_set.contains(&flags[0])); assert!(flags_set.contains(&flags[1])); } #[test] fn test_read_flag_from_wrong_extended_file_err() { let flags = create_test_flags(); // Create the file /35/extended_flags_list.txt let temp_dir = tempdir().unwrap(); let mut file_path = temp_dir.path().to_path_buf(); file_path.push("35"); fs::create_dir_all(&file_path).unwrap(); file_path.push("extended_flags_list.txt"); let mut file = File::create(&file_path).unwrap(); // Write all flags to the file. add_flags_to_file(&mut file, &[flags[0].clone(), flags[1].clone()]); let err = read_extend_file_to_map_using_path(file_path.to_string_lossy().to_string()) .unwrap_err(); assert_eq!( format!("{:?}", err), "Provided incorrect file, must be extended_flags_list_35.txt" ); } #[test] fn test_parse_full_version_correct_input_major_dot_minor() { let version = parse_full_version("12.34".to_string()); assert!(version.is_ok()); assert_eq!(version.unwrap(), 1_200_034); } #[test] fn test_parse_full_version_correct_input_omit_dot_minor() { let version = parse_full_version("1234".to_string()); assert!(version.is_ok()); assert_eq!(version.unwrap(), 123_400_000); } #[test] fn test_parse_full_version_incorrect_input_empty_string() { let version = parse_full_version("".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_no_numbers_in_string() { let version = parse_full_version("hello".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_unexpected_patch_version() { let version = parse_full_version("1.2.3".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_leading_dot_missing_major_version() { let version = parse_full_version(".1234".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_trailing_dot_missing_minor_version() { let version = parse_full_version("1234.".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_negative_major_version() { let version = parse_full_version("-12.34".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_negative_minor_version() { let version = parse_full_version("12.-34".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_major_version_too_large() { let version = parse_full_version("40000.1".to_string()); assert!(version.is_err()); } #[test] fn test_parse_full_version_incorrect_input_minor_version_too_large() { let version = parse_full_version("3.99999999".to_string()); assert!(version.is_err()); } } ================================================ FILE: tools/aconfig/convert_finalized_flags/src/main.rs ================================================ /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! convert_finalized_flags is a build time tool used to convert the finalized //! flags text files under prebuilts/sdk into structured data (FinalizedFlag //! struct). //! This binary is intended to run as part of a genrule to create a json file //! which is provided to the aconfig binary that creates the codegen. //! Usage: //! cargo run -- --flag-files-path path/to/prebuilts/sdk/finalized-flags.txt file2.txt etc use anyhow::Result; use clap::Parser; use convert_finalized_flags::{ read_extend_file_to_map_using_path, read_files_to_map_using_path, EXTENDED_FLAGS_35_APILEVEL, }; const ABOUT_TEXT: &str = "Tool for processing finalized-flags.txt files. These files contain the list of qualified flag names that have been finalized, each on a newline. The directory of the flag file is the finalized API level. The output is a json map of API level to set of FinalizedFlag objects. The only supported use case for this tool is via a genrule at build time for aconfig codegen. Args: * `flag-files-path`: Space-separated list of absolute paths for the finalized flags files. "; #[derive(Parser, Debug)] #[clap(long_about=ABOUT_TEXT, bin_name="convert-finalized-flags")] struct Cli { /// Flags files. #[arg(long = "flag_file_path")] flag_file_path: Vec, #[arg(long)] extended_flag_file_path: String, } fn main() -> Result<()> { let cli = Cli::parse(); let mut finalized_flags_map = read_files_to_map_using_path(cli.flag_file_path)?; let extended_flag_set = read_extend_file_to_map_using_path(cli.extended_flag_file_path)?; for flag in extended_flag_set { finalized_flags_map.insert_if_new(EXTENDED_FLAGS_35_APILEVEL, flag); } let json_str = serde_json::to_string(&finalized_flags_map)?; println!("{}", json_str); Ok(()) } ================================================ FILE: tools/aconfig/exported_flag_check/Android.bp ================================================ package { default_applicable_licenses: ["Android-Apache-2.0"], } rust_defaults { name: "exported-flag-check-defaults", edition: "2021", clippy_lints: "android", lints: "android", srcs: ["src/main.rs"], rustlibs: [ "libaconfig_protos", "libanyhow", "libclap", "libregex", ], } rust_binary_host { name: "exported-flag-check", defaults: ["record-finalized-flags-defaults"], } rust_test_host { name: "exported-flag-check-test", defaults: ["record-finalized-flags-defaults"], test_suites: ["general-tests"], } ================================================ FILE: tools/aconfig/exported_flag_check/Cargo.toml ================================================ [package] name = "exported-flag-check" version = "0.1.0" edition = "2021" [features] default = ["cargo"] cargo = [] [dependencies] aconfig_protos = { path = "../aconfig_protos" } anyhow = "1.0.69" clap = { version = "4.1.8", features = ["derive"] } regex = "1.11.1" ================================================ FILE: tools/aconfig/exported_flag_check/allow_flag_list.txt ================================================ android.adpf.adpf_viewrootimpl_action_down_boost android.app.admin.flags.coexistence_migration_for_supervision_enabled android.app.admin.flags.enable_supervision_service_sync android.app.admin.flags.lock_now_coexistence android.app.admin.flags.permission_migration_for_zero_trust_api_enabled android.app.admin.flags.reset_password_with_token_coexistence android.app.admin.flags.set_application_restrictions_coexistence android.app.admin.flags.set_backup_service_enabled_coexistence android.app.admin.flags.set_keyguard_disabled_features_coexistence android.app.admin.flags.set_permission_grant_state_coexistence android.app.app_restrictions_api android.app.enforce_pic_testmode_protocol android.app.job.backup_jobs_exemption android.app.pic_uses_shared_memory android.app.pinner_service_client_api android.app.supervision.flags.deprecate_dpm_supervision_apis android.app.supervision.flags.enable_sync_with_dpm android.app.supervision.flags.supervision_api android.app.supervision.flags.supervision_api_on_wear android.app.ui_rich_ongoing android.appwidget.flags.use_smaller_app_widget_system_radius android.car.feature.always_send_initial_value_event android.car.feature.android_b_vehicle_properties android.car.feature.android_vic_vehicle_properties android.car.feature.area_id_config_access android.car.feature.async_audio_service_init android.car.feature.audio_control_hal_configuration android.car.feature.audio_legacy_mode_navigation_volume android.car.feature.audio_vendor_freeze_improvements android.car.feature.batched_subscriptions android.car.feature.car_app_card android.car.feature.car_audio_dynamic_devices android.car.feature.car_audio_fade_manager_configuration android.car.feature.car_audio_min_max_activation_volume android.car.feature.car_audio_mute_ambiguity android.car.feature.car_evs_query_service_status android.car.feature.car_evs_stream_management android.car.feature.car_night_global_setting android.car.feature.car_power_cancel_shell_command android.car.feature.car_property_detailed_error_codes android.car.feature.car_property_supported_value android.car.feature.car_property_value_property_status android.car.feature.cluster_health_monitoring android.car.feature.display_compatibility android.car.feature.handle_property_events_in_binder_thread android.car.feature.persist_ap_settings android.car.feature.projection_query_bt_profile_inhibit android.car.feature.serverless_remote_access android.car.feature.subscription_with_resolution android.car.feature.supports_secure_passenger_users android.car.feature.switch_user_ignoring_uxr android.car.feature.variable_update_rate android.car.feature.visible_background_user_restrictions android.companion.new_association_builder android.companion.ongoing_perm_sync android.companion.virtualdevice.flags.camera_multiple_input_streams android.companion.virtualdevice.flags.notifications_for_device_streaming android.content.pm.get_package_storage_stats android.content.res.layout_readwrite_flags android.content.res.resources_minor_version_support android.content.res.rro_control_for_android_no_overlayable android.content.res.self_targeting_android_resource_frro android.content.res.system_context_handle_app_info_changed android.credentials.flags.settings_activity_enabled android.hardware.biometrics.screen_off_unlock_udfps android.hardware.devicestate.feature.flags.device_state_property_migration android.hardware.devicestate.feature.flags.device_state_rdm_v2 android.hardware.devicestate.feature.flags.device_state_requester_cancel_state android.hardware.usb.flags.enable_interface_name_device_filter android.hardware.usb.flags.enable_is_mode_change_supported_api android.media.audio.focus_exclusive_with_recording android.media.audio.focus_freeze_test_api android.media.audio.foreground_audio_control android.media.audio.hardening_permission_api android.media.audio.hardening_permission_spa android.media.audio.ro_foreground_audio_control android.media.audiopolicy.audio_mix_test_api android.media.codec.aidl_hal_input_surface android.media.swcodec.flags.apv_software_codec android.media.swcodec.flags.mpeg2_keep_threads_active android.media.tv.flags.enable_le_audio_broadcast_ui android.media.tv.flags.enable_le_audio_unicast_ui android.media.tv.flags.hdmi_control_collect_physical_address android.media.tv.flags.hdmi_control_enhanced_behavior android.media.tv.flags.tif_unbind_inactive_tis android.multiuser.enable_biometrics_to_unlock_private_space android.net.platform.flags.mdns_improvement_for_25q2 android.nfc.nfc_persist_log android.nfc.nfc_watchdog android.os.adpf_graphics_pipeline android.os.android_os_build_vanilla_ice_cream android.os.battery_saver_supported_check_api android.os.network_time_uses_shared_memory android.os.profiling.persist_queue android.os.profiling.redaction_enabled android.permission.flags.allow_host_permission_dialogs_on_virtual_devices android.permission.flags.device_aware_permissions_enabled android.permission.flags.device_policy_management_role_split_create_managed_profile_enabled android.permission.flags.enable_aiai_proxied_text_classifiers android.permission.flags.enable_otp_in_text_classifiers android.permission.flags.enable_sqlite_appops_accesses android.permission.flags.location_bypass_privacy_dashboard_enabled android.permission.flags.note_op_batching_enabled android.permission.flags.permission_request_short_circuit_enabled android.permission.flags.rate_limit_batched_note_op_async_callbacks_enabled android.permission.flags.sensitive_notification_app_protection android.permission.flags.supervision_role_permission_update_enabled android.permission.flags.unknown_call_package_install_blocking_enabled android.permission.flags.updatable_text_classifier_for_otp_detection_enabled android.permission.flags.use_profile_labels_for_default_app_section_titles android.permission.flags.wallet_role_cross_user_enabled android.provider.allow_config_maximum_call_log_entries_per_sim android.provider.backup_tasks_settings_screen android.provider.flags.new_storage_writer_system_api android.service.autofill.fill_dialog_improvements_impl android.service.chooser.fix_resolver_memory_leak android.service.notification.redact_sensitive_notifications_big_text_style android.service.notification.redact_sensitive_notifications_from_untrusted_listeners android.view.accessibility.motion_event_observing android.view.flags.expected_presentation_time_api android.view.flags.toolkit_frame_rate_touch_boost_25q1 android.view.inputmethod.concurrent_input_methods android.view.inputmethod.ime_switcher_revamp android.view.inputmethod.imm_userhandle_hostsidetests android.webkit.mainline_apis android.widget.flags.use_wear_material3_ui com.android.aconfig.test.disabled_rw_exported com.android.aconfig.test.enabled_fixed_ro_exported com.android.aconfig.test.enabled_ro_exported com.android.aconfig.test.exported.exported_flag com.android.aconfig.test.forcereadonly.fro_exported com.android.adservices.ondevicepersonalization.flags.on_device_personalization_apis_enabled com.android.appsearch.flags.app_open_event_indexer_enabled com.android.appsearch.flags.apps_indexer_enabled com.android.appsearch.flags.enable_app_functions_schema_parser com.android.appsearch.flags.enable_apps_indexer_incremental_put com.android.appsearch.flags.enable_contacts_index_first_middle_and_last_names com.android.appsearch.flags.enable_document_limiter_replace_tracking com.android.appsearch.flags.enable_enterprise_empty_batch_result_fix com.android.bluetooth.flags.allow_switching_hid_and_hogp com.android.bluetooth.flags.bt_offload_socket_api com.android.bluetooth.flags.channel_sounding com.android.bluetooth.flags.fix_started_module_race com.android.bluetooth.flags.le_subrate_api com.android.bluetooth.flags.leaudio_broadcast_monitor_source_sync_status com.android.bluetooth.flags.leaudio_broadcast_volume_control_for_connected_devices com.android.bluetooth.flags.leaudio_multiple_vocs_instances_api com.android.bluetooth.flags.metadata_api_inactive_audio_device_upon_connection com.android.bluetooth.flags.settings_can_control_hap_preset com.android.bluetooth.flags.unix_file_socket_creation_failure com.android.graphics.flags.icon_load_drawable_return_null_when_uri_decode_fails com.android.graphics.hwui.flags.animated_image_drawable_filter_bitmap com.android.hardware.input.manage_key_gestures com.android.healthfitness.flags.activity_intensity_db com.android.healthfitness.flags.add_missing_access_logs com.android.healthfitness.flags.architecture_improvement com.android.healthfitness.flags.cloud_backup_and_restore com.android.healthfitness.flags.cycle_phases com.android.healthfitness.flags.d2d_file_deletion_bug_fix com.android.healthfitness.flags.dependency_injection com.android.healthfitness.flags.development_database com.android.healthfitness.flags.ecosystem_metrics com.android.healthfitness.flags.ecosystem_metrics_db_changes com.android.healthfitness.flags.export_import com.android.healthfitness.flags.export_import_fast_follow com.android.healthfitness.flags.export_import_nice_to_have com.android.healthfitness.flags.expressive_theming_enabled com.android.healthfitness.flags.health_connect_mappings com.android.healthfitness.flags.immediate_export com.android.healthfitness.flags.logcat_censor_iae com.android.healthfitness.flags.new_information_architecture com.android.healthfitness.flags.onboarding com.android.healthfitness.flags.permission_metrics com.android.healthfitness.flags.permission_tracker_fix_mapping_init com.android.healthfitness.flags.personal_health_record_database com.android.healthfitness.flags.personal_health_record_disable_d2d com.android.healthfitness.flags.personal_health_record_disable_export_import com.android.healthfitness.flags.personal_health_record_enable_d2d_and_export_import com.android.healthfitness.flags.personal_health_record_entries_screen com.android.healthfitness.flags.personal_health_record_lock_screen_banner com.android.healthfitness.flags.personal_health_record_telemetry com.android.healthfitness.flags.personal_health_record_telemetry_private_ww com.android.healthfitness.flags.personal_health_record_ui_telemetry com.android.healthfitness.flags.phr_fhir_basic_complex_type_validation com.android.healthfitness.flags.phr_fhir_complex_type_validation com.android.healthfitness.flags.phr_fhir_oneof_validation com.android.healthfitness.flags.phr_fhir_primitive_type_validation com.android.healthfitness.flags.phr_fhir_structural_validation com.android.healthfitness.flags.phr_read_medical_resources_fix_query_limit com.android.healthfitness.flags.phr_upsert_fix_parcel_size_calculation com.android.healthfitness.flags.phr_upsert_fix_use_shared_memory com.android.icu.icu_v_api com.android.internal.telephony.flags.async_init_carrier_privileges_tracker com.android.internal.telephony.flags.cleanup_carrier_app_update_enabled_state_logic com.android.internal.telephony.flags.oem_enabled_satellite_phase_2 com.android.internal.telephony.flags.remap_disconnect_cause_sip_request_cancelled com.android.libcore.hpke_v_apis com.android.libcore.read_only_dynamic_code_load com.android.libcore.v_apis com.android.media.audio.hardening_impl com.android.media.audio.hardening_strict com.android.media.extractor.flags.extractor_mp4_enable_apv com.android.media.extractor.flags.extractor_sniff_midi_optimizations com.android.media.flags.enable_cross_user_routing_in_media_router2 com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change com.android.media.metrics.flags.mediametrics_to_module com.android.media.projection.flags.media_projection_connected_display com.android.media.projection.flags.media_projection_connected_display_no_virtual_device com.android.net.ct.flags.certificate_transparency_job com.android.net.ct.flags.certificate_transparency_service com.android.net.flags.restrict_local_network com.android.net.flags.tethering_active_sessions_metrics com.android.net.thread.flags.thread_mobile_enabled com.android.nfc.module.flags.nfc_hce_latency_events com.android.org.conscrypt.flags.certificate_transparency_checkservertrusted_api com.android.permission.flags.add_banners_to_privacy_sensitive_apps_for_aaos com.android.permission.flags.app_permission_fragment_uses_preferences com.android.permission.flags.archiving_read_only com.android.permission.flags.decluttered_permission_manager_enabled com.android.permission.flags.enable_coarse_fine_location_prompt_for_aaos com.android.permission.flags.enhanced_confirmation_backport_enabled com.android.permission.flags.expressive_design_enabled com.android.permission.flags.livedata_refactor_permission_timeline_enabled com.android.permission.flags.odad_notifications_supported com.android.permission.flags.permission_timeline_attribution_label_fix com.android.permission.flags.private_profile_supported com.android.permission.flags.safety_center_enabled_no_device_config com.android.permission.flags.safety_center_issue_only_affects_group_status com.android.permission.flags.wear_compose_material3 com.android.permission.flags.wear_privacy_dashboard_enabled_read_only com.android.providers.contactkeys.flags.contactkeys_strip_fix com.android.providers.media.flags.enable_backup_and_restore com.android.providers.media.flags.enable_malicious_app_detector com.android.providers.media.flags.enable_mark_media_as_favorite_api com.android.providers.media.flags.enable_modern_photopicker com.android.providers.media.flags.enable_photopicker_search com.android.providers.media.flags.enable_photopicker_transcoding com.android.providers.media.flags.enable_stable_uris_for_external_primary_volume com.android.providers.media.flags.enable_stable_uris_for_public_volume com.android.providers.media.flags.enable_unicode_check com.android.providers.media.flags.index_media_latitude_longitude com.android.providers.media.flags.version_lockdown com.android.ranging.flags.ranging_stack_updates_25q4 com.android.server.backup.enable_read_all_external_storage_files com.android.server.telecom.flags.allow_system_apps_resolve_voip_calls com.android.server.telecom.flags.telecom_app_label_proxy_hsum_aware com.android.server.telecom.flags.telecom_main_user_in_block_check com.android.server.telecom.flags.telecom_main_user_in_get_respond_message_app com.android.server.updates.certificate_transparency_installer com.android.system.virtualmachine.flags.terminal_gui_support com.android.tradeinmode.flags.enable_trade_in_mode com.android.update_engine.minor_changes_2025q4 com.android.uwb.flags.uwb_fira_3_0_25q4 com.android.wifi.flags.network_provider_battery_charging_status com.android.wifi.flags.p2p_dialog2 com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api com.android.wifi.flags.wep_disabled_in_apm com.android.window.flags.untrusted_embedding_state_sharing vendor.vibrator.hal.flags.enable_pwle_v2 vendor.vibrator.hal.flags.remove_capo ================================================ FILE: tools/aconfig/exported_flag_check/allow_package_list.txt ================================================ ================================================ FILE: tools/aconfig/exported_flag_check/src/main.rs ================================================ /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! `exported-flag-check` is a tool to ensures that exported flags are used as intended use anyhow::{ensure, Result}; use clap::Parser; use std::{collections::HashSet, fs::File, path::PathBuf}; mod utils; use utils::{ check_all_exported_flags, extract_flagged_api_flags, get_exported_flags_from_binary_proto, read_finalized_flags, }; const ABOUT: &str = "CCheck Exported Flags This tool ensures that exported flags are used as intended. Exported flags, marked with `is_exported: true` in their declaration, are designed to control access to specific API features. This tool identifies and reports any exported flags that are not currently associated with an API feature, preventing unnecessary flag proliferation and maintaining a clear API design. This tool works as follows: - Read API signature files from source tree (*current.txt files) [--api-signature-file] - Read the current aconfig flag values from source tree [--parsed-flags-file] - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file] - Extract the flags slated for API by scanning through the API signature files - Merge the found flags with the recorded flags from previous API finalizations - Error if exported flags are not in the set "; #[derive(Parser, Debug)] #[clap(about=ABOUT)] struct Cli { #[arg(long)] parsed_flags_file: PathBuf, #[arg(long)] api_signature_file: Vec, #[arg(long)] finalized_flags_file: PathBuf, } fn main() -> Result<()> { let args = Cli::parse(); let mut flags_used_with_flaggedapi_annotation = HashSet::new(); for path in &args.api_signature_file { let file = File::open(path)?; let flags = extract_flagged_api_flags(file)?; flags_used_with_flaggedapi_annotation.extend(flags); } let file = File::open(args.parsed_flags_file)?; let all_flags = get_exported_flags_from_binary_proto(file)?; let file = File::open(args.finalized_flags_file)?; let already_finalized_flags = read_finalized_flags(file)?; let exported_flags = check_all_exported_flags( &flags_used_with_flaggedapi_annotation, &all_flags, &already_finalized_flags, )?; println!("{}", exported_flags.join("\n")); ensure!( exported_flags.is_empty(), "Flags {} are exported but not used to guard any API. \ Exported flag should be used to guard API", exported_flags.join(",") ); Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test() { let input = include_bytes!("../tests/api-signature-file.txt"); let flags_used_with_flaggedapi_annotation = extract_flagged_api_flags(&input[..]).unwrap(); let input = include_bytes!("../tests/flags.protobuf"); let all_flags_to_be_finalized = get_exported_flags_from_binary_proto(&input[..]).unwrap(); let input = include_bytes!("../tests/finalized-flags.txt"); let already_finalized_flags = read_finalized_flags(&input[..]).unwrap(); let exported_flags = check_all_exported_flags( &flags_used_with_flaggedapi_annotation, &all_flags_to_be_finalized, &already_finalized_flags, ) .unwrap(); assert_eq!(1, exported_flags.len()); } } ================================================ FILE: tools/aconfig/exported_flag_check/src/utils.rs ================================================ /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use aconfig_protos::ParsedFlagExt; use anyhow::{anyhow, Context, Result}; use regex::Regex; use std::{ collections::HashSet, io::{BufRead, BufReader, Read}, }; pub(crate) type FlagId = String; /// Grep for all flags used with @FlaggedApi annotations in an API signature file (*current.txt /// file). pub(crate) fn extract_flagged_api_flags(mut reader: R) -> Result> { let mut haystack = String::new(); reader.read_to_string(&mut haystack)?; let regex = Regex::new(r#"(?ms)@FlaggedApi\("(.*?)"\)"#).unwrap(); let iter = regex.captures_iter(&haystack).map(|cap| cap[1].to_owned()); Ok(HashSet::from_iter(iter)) } /// Read a list of flag names. The input is expected to be plain text, with each line containing /// the name of a single flag. pub(crate) fn read_finalized_flags(reader: R) -> Result> { BufReader::new(reader) .lines() .map(|line_result| line_result.context("Failed to read line from finalized flags file")) .collect() } /// Parse a ProtoParsedFlags binary protobuf blob and return the fully qualified names of flags /// have is_exported as true. pub(crate) fn get_exported_flags_from_binary_proto( mut reader: R, ) -> Result> { let mut buffer = Vec::new(); reader.read_to_end(&mut buffer)?; let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer) .map_err(|_| anyhow!("failed to parse binary proto"))?; let iter = parsed_flags .parsed_flag .into_iter() .filter(|flag| flag.is_exported()) .map(|flag| flag.fully_qualified_name()); Ok(HashSet::from_iter(iter)) } fn get_allow_flag_list() -> Result> { let allow_list: HashSet = include_str!("../allow_flag_list.txt").lines().map(|x| x.into()).collect(); Ok(allow_list) } fn get_allow_package_list() -> Result> { let allow_list: HashSet = include_str!("../allow_package_list.txt").lines().map(|x| x.into()).collect(); Ok(allow_list) } /// Filter out the flags have is_exported as true but not used with @FlaggedApi annotations /// in the source tree, or in the previously finalized flags set. pub(crate) fn check_all_exported_flags( flags_used_with_flaggedapi_annotation: &HashSet, all_flags: &HashSet, already_finalized_flags: &HashSet, ) -> Result> { let allow_flag_list = get_allow_flag_list()?; let allow_package_list = get_allow_package_list()?; let new_flags: Vec = all_flags .difference(flags_used_with_flaggedapi_annotation) .cloned() .collect::>() .difference(already_finalized_flags) .cloned() .collect::>() .difference(&allow_flag_list) .filter(|flag| { if let Some(last_dot_index) = flag.rfind('.') { let package_name = &flag[..last_dot_index]; !allow_package_list.contains(package_name) } else { true } }) .cloned() .collect(); Ok(new_flags) } #[cfg(test)] mod tests { use super::*; #[test] fn test_extract_flagged_api_flags() { let api_signature_file = include_bytes!("../tests/api-signature-file.txt"); let flags = extract_flagged_api_flags(&api_signature_file[..]).unwrap(); assert_eq!( flags, HashSet::from_iter(vec![ "record_finalized_flags.test.foo".to_string(), "this.flag.is.not.used".to_string(), ]) ); } #[test] fn test_read_finalized_flags() { let input = include_bytes!("../tests/finalized-flags.txt"); let flags = read_finalized_flags(&input[..]).unwrap(); assert_eq!( flags, HashSet::from_iter(vec![ "record_finalized_flags.test.bar".to_string(), "record_finalized_flags.test.baz".to_string(), ]) ); } #[test] fn test_disabled_or_read_write_flags_are_ignored() { let bytes = include_bytes!("../tests/flags.protobuf"); let flags = get_exported_flags_from_binary_proto(&bytes[..]).unwrap(); assert_eq!( flags, HashSet::from_iter(vec![ "record_finalized_flags.test.foo".to_string(), "record_finalized_flags.test.not_enabled".to_string() ]) ); } } ================================================ FILE: tools/aconfig/exported_flag_check/tests/api-signature-file.txt ================================================ // Signature format: 2.0 package android { public final class C { ctor public C(); } public static final class C.inner { ctor public C.inner(); field @FlaggedApi("record_finalized_flags.test.foo") public static final String FOO = "foo"; field @FlaggedApi("this.flag.is.not.used") public static final String BAR = "bar"; } } ================================================ FILE: tools/aconfig/exported_flag_check/tests/finalized-flags.txt ================================================ record_finalized_flags.test.bar record_finalized_flags.test.baz ================================================ FILE: tools/aconfig/exported_flag_check/tests/flags.declarations ================================================ package: "record_finalized_flags.test" container: "system" flag { name: "foo" namespace: "test" description: "FIXME" bug: "" is_exported:true } flag { name: "not_enabled" namespace: "test" description: "FIXME" bug: "" is_exported:true } ================================================ FILE: tools/aconfig/exported_flag_check/tests/flags.values ================================================ flag_value { package: "record_finalized_flags.test" name: "foo" state: ENABLED permission: READ_ONLY } flag_value { package: "record_finalized_flags.test" name: "not_enabled" state: DISABLED permission: READ_ONLY } ================================================ FILE: tools/aconfig/exported_flag_check/tests/generate-flags-protobuf.sh ================================================ #!/bin/bash aconfig create-cache \ --package record_finalized_flags.test \ --container system \ --declarations flags.declarations \ --values flags.values \ --cache flags.protobuf ================================================ FILE: tools/aconfig/fake_device_config/Android.bp ================================================ // Copyright (C) 2023 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. java_library { name: "fake_device_config", srcs: [ "src/**/*.java", ], sdk_version: "none", system_modules: "core-all-system-modules", host_supported: true, is_stubs_module: true, } java_library { name: "aconfig_storage_stub", srcs: [ "src/android/os/flagging/**/*.java", ], sdk_version: "core_current", host_supported: true, is_stubs_module: true, } java_library { name: "aconfig_storage_stub_none", srcs: [ "src/android/os/flagging/**/*.java", ], sdk_version: "none", system_modules: "core-all-system-modules", host_supported: true, is_stubs_module: true, } ================================================ FILE: tools/aconfig/fake_device_config/src/android/os/Build.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; public class Build { public static class VERSION { public static final int SDK_INT = placeholder(); private static int placeholder() { throw new UnsupportedOperationException("Stub!"); } } } ================================================ FILE: tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackage.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; /* * This class allows generated aconfig code to compile independently of the framework. */ public class AconfigPackage { public static AconfigPackage load(String packageName) { throw new UnsupportedOperationException("Stub!"); } public boolean getBooleanFlagValue(String flagName, boolean defaultValue) { throw new UnsupportedOperationException("Stub!"); } } ================================================ FILE: tools/aconfig/fake_device_config/src/android/os/flagging/AconfigPackageInternal.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; /* * This class allows generated aconfig code to compile independently of the framework. */ public class AconfigPackageInternal { public static AconfigPackageInternal load(String packageName, long packageFingerprint) { throw new UnsupportedOperationException("Stub!"); } public boolean getBooleanFlagValue(int index) { throw new UnsupportedOperationException("Stub!"); } } ================================================ FILE: tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; import java.util.Set; /* * This class allows generated aconfig code to compile independently of the framework. */ public class PlatformAconfigPackage { public static final Set PLATFORM_PACKAGE_MAP_FILES = Set.of( "system.package.map", "system_ext.package.map", "vendor.package.map", "product.package.map"); public static PlatformAconfigPackage load(String packageName) { throw new UnsupportedOperationException("Stub!"); } public boolean getBooleanFlagValue(String flagName, boolean defaultValue) { throw new UnsupportedOperationException("Stub!"); } } ================================================ FILE: tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackageInternal.java ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os.flagging; /* * This class allows generated aconfig code to compile independently of the framework. */ public class PlatformAconfigPackageInternal { public static PlatformAconfigPackageInternal load(String packageName, long packageFingerprint) { throw new UnsupportedOperationException("Stub!"); } public boolean getBooleanFlagValue(int index) { throw new UnsupportedOperationException("Stub!"); } } ================================================ FILE: tools/aconfig/fake_device_config/src/android/util/Log.java ================================================ package android.util; public final class Log { public static int i(String tag, String msg) { throw new UnsupportedOperationException("Stub!"); } public static int w(String tag, String msg) { throw new UnsupportedOperationException("Stub!"); } public static int e(String tag, String msg) { throw new UnsupportedOperationException("Stub!"); } public static int e(String tag, String msg, Throwable tr) { throw new UnsupportedOperationException("Stub!"); } } ================================================ FILE: tools/aconfig/overrideflags/overrideflags.py ================================================ #!/usr/bin/env python3 # # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Create Aconfig value building rules. This script will help to create Aconfig flag value building rules. It will parse necessary information in the value file to create the building rules, but it will not validate the value file. The validation will defer to the building system. """ import argparse import pathlib import re import sys _VALUE_LIST_TEMPLATE: str = """ ACONFIG_VALUES_LIST_LOCAL = [{}] """ _ACONFIG_VALUES_TEMPLATE: str = """ aconfig_values {{ name: "{}", package: "{}", srcs: [ "{}", ] }} """ _ACONFIG_VALUES_NAME_SUFFIX: str = "aconfig-local-override-{}" _PACKAGE_REGEX = re.compile(r"^package\:\s*\"([\w\d\.]+)\"") _ANDROID_BP_FILE_NAME = r"Android.bp" def _parse_packages(file: pathlib.Path) -> set[str]: packages = set() with open(file) as f: for line in f: line = line.strip() package_match = _PACKAGE_REGEX.match(line) if package_match is None: continue package_name = package_match.group(1) packages.add(package_name) return packages def _create_android_bp(packages: set[str], file_name: str) -> str: android_bp = "" value_list = ",\n ".join( map(f'"{_ACONFIG_VALUES_NAME_SUFFIX}"'.format, packages) ) if value_list: value_list = "\n " + value_list + "\n" android_bp += _VALUE_LIST_TEMPLATE.format(value_list) + "\n" for package in packages: android_bp += _ACONFIG_VALUES_TEMPLATE.format( _ACONFIG_VALUES_NAME_SUFFIX.format(package), package, file_name ) android_bp += "\n" return android_bp def _write_android_bp(new_android_bp: str, out: pathlib.Path) -> None: if not out.is_dir(): out.mkdir(parents=True, exist_ok=True) output = out.joinpath(_ANDROID_BP_FILE_NAME) with open(output, "r+", encoding="utf8") as file: lines = [] for line in file: line = line.rstrip("\n") if line.startswith("ACONFIG_VALUES_LIST_LOCAL"): break lines.append(line) # Overwrite the file with the updated contents. file.seek(0) file.truncate() file.write("\n".join(lines)) file.write(new_android_bp) def main(args): """Program entry point.""" args_parser = argparse.ArgumentParser() args_parser.add_argument( "--overrides", required=True, help="The path to override file.", ) args_parser.add_argument( "--out", required=True, help="The path to output directory.", ) args = args_parser.parse_args(args) file = pathlib.Path(args.overrides) out = pathlib.Path(args.out) if not file.is_file(): raise FileNotFoundError(f"File '{file}' is not found") packages = _parse_packages(file) new_android_bp = _create_android_bp(packages, file.name) _write_android_bp(new_android_bp, out) if __name__ == "__main__": main(sys.argv[1:]) ================================================ FILE: tools/acp/Android.bp ================================================ // Copyright 2005 The Android Open Source Project // // Custom version of cp. package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } cc_binary_host { srcs: ["acp.c"], cflags: ["-Wall", "-Werror"], static_libs: ["libhost"], name: "acp", stl: "none", } ================================================ FILE: tools/acp/README ================================================ README for Android "acp" Command The "cp" command was judged and found wanting. The issues are: Mac OS X: - Uses the BSD cp, not the fancy GNU cp. It lacks the "-u" flag, which only copies files if they are newer than the destination. This can slow the build when copying lots of content. - Doesn't take the "-d" flag, which causes symlinks to be copied as links. This is the default behavior, so it's not all bad, but it complains if you supply "-d". MinGW/Cygwin: - Gets really weird when copying a file called "foo.exe", failing with "cp: skipping file 'foo.exe', as it was replaced while being copied". This only seems to happen when the source file is on an NFS/Samba volume. "cp" works okay copying from local disk. Linux: - On some systems it's possible to have microsecond-accurate timestamps on an NFS volume, and non-microsecond timestamps on a local volume. If you copy from NFS to local disk, your NFS files will always be newer, because the local disk time stamp is truncated rather than rounded up. This foils the "-u" flag if you also supply the "-p" flag to preserve timestamps. - The Darwin linker insists that ranlib be current. If you copy the library, the time stamp no longer matches. Preserving the time stamp is essential, so simply turning the "-p" flag off doesn't work. Futzing around these in make with GNU make functions is awkward at best. It's easier and more reliable to write a cp command that works properly. The "acp" command takes most of the standard flags, following the GNU conventions. It adds a "-e" flag, used when copying executables around. On most systems it is ignored, but on MinGW/Cygwin it allows "cp foo bar" to work when what is actually meant is "cp foo.exe bar.exe". Unlike the default Cygwin cp, "acp foo bar" will not find foo.exe unless you add the "-e" flag, avoiding potential ambiguity. ================================================ FILE: tools/acp/acp.c ================================================ /* * Copyright 2005 The Android Open Source Project * * Android "cp" replacement. * * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead * of utime(), and getxattr()/setxattr() instead of chmod(). These are * probably "better", but are non-portable, and not necessary for our * purposes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*#define DEBUG_MSGS*/ #ifdef DEBUG_MSGS # define DBUG(x) printf x #else # define DBUG(x) ((void)0) #endif #define FSSEP '/' /* filename separator char */ /* * Process the command-line file arguments. * * Returns 0 on success. */ int process(int argc, char* const argv[], unsigned int options) { int retVal = 0; int i; char* stripDest = NULL; int stripDestLen; bool destMustBeDir = false; struct stat sb; assert(argc >= 2); /* * Check for and trim a trailing slash on the last arg. * * It's useful to be able to say "cp foo bar/" when you want to copy * a single file into a directory. If you say "cp foo bar", and "bar" * does not exist, it will create "bar", when what you really wanted * was for the cp command to fail with "directory does not exist". */ stripDestLen = strlen(argv[argc-1]); stripDest = malloc(stripDestLen+1); memcpy(stripDest, argv[argc-1], stripDestLen+1); if (stripDest[stripDestLen-1] == FSSEP) { stripDest[--stripDestLen] = '\0'; destMustBeDir = true; } if (argc > 2) destMustBeDir = true; /* * Start with a quick check to ensure that, if we're expecting to copy * to a directory, the target already exists and is actually a directory. * It's okay if it's a symlink to a directory. * * If it turns out to be a directory, go ahead and raise the * destMustBeDir flag so we do some path concatenation below. */ if (stat(stripDest, &sb) < 0) { if (destMustBeDir) { if (errno == ENOENT) fprintf(stderr, "acp: destination directory '%s' does not exist\n", stripDest); else fprintf(stderr, "acp: unable to stat dest dir\n"); retVal = 1; goto bail; } } else { if (S_ISDIR(sb.st_mode)) { DBUG(("--- dest exists and is a dir, setting flag\n")); destMustBeDir = true; } else if (destMustBeDir) { fprintf(stderr, "acp: destination '%s' is not a directory\n", stripDest); retVal = 1; goto bail; } } /* * Copying files. * * Strip trailing slashes off. They shouldn't be there, but * sometimes file completion will put them in for directories. * * The observed behavior of GNU and BSD cp is that they print warnings * if something fails, but continue on. If any part fails, the command * exits with an error status. */ for (i = 0; i < argc-1; i++) { const char* srcName; char* src; char* dst; int copyResult; int srcLen; /* make a copy of the source name, and strip trailing '/' */ srcLen = strlen(argv[i]); src = malloc(srcLen+1); memcpy(src, argv[i], srcLen+1); if (src[srcLen-1] == FSSEP) src[--srcLen] = '\0'; /* find just the name part */ srcName = strrchr(src, FSSEP); if (srcName == NULL) { srcName = src; } else { srcName++; assert(*srcName != '\0'); } if (destMustBeDir) { /* concatenate dest dir and src name */ int srcNameLen = strlen(srcName); dst = malloc(stripDestLen +1 + srcNameLen +1); memcpy(dst, stripDest, stripDestLen); dst[stripDestLen] = FSSEP; memcpy(dst + stripDestLen+1, srcName, srcNameLen+1); } else { /* simple */ dst = stripDest; } /* * Copy the source to the destination. */ copyResult = copyFile(src, dst, options); if (copyResult != 0) retVal = 1; free(src); if (dst != stripDest) free(dst); } bail: free(stripDest); return retVal; } /* * Set up the options. */ int main(int argc, char* const argv[]) { bool wantUsage; int ic, retVal; int verboseLevel; unsigned int options; verboseLevel = 0; options = 0; wantUsage = false; while (1) { ic = getopt(argc, argv, "defprtuv"); if (ic < 0) break; switch (ic) { case 'd': options |= COPY_NO_DEREFERENCE; break; case 'e': options |= COPY_TRY_EXE; break; case 'f': options |= COPY_FORCE; break; case 'p': options |= COPY_PERMISSIONS; break; case 't': options |= COPY_TIMESTAMPS; break; case 'r': options |= COPY_RECURSIVE; break; case 'u': options |= COPY_UPDATE_ONLY; break; case 'v': verboseLevel++; break; default: fprintf(stderr, "Unexpected arg -%c\n", ic); wantUsage = true; break; } if (wantUsage) break; } options |= verboseLevel & COPY_VERBOSE_MASK; if (optind == argc-1) { fprintf(stderr, "acp: missing destination file\n"); return 2; } else if (optind+2 > argc) wantUsage = true; if (wantUsage) { fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n"); fprintf(stderr, " or: acp [OPTION]... SOURCE... DIRECTORY\n"); fprintf(stderr, "\nOptions:\n"); fprintf(stderr, " -d never follow (dereference) symbolic links\n"); fprintf(stderr, " -e if source file doesn't exist, try adding " "'.exe' [Win32 only]\n"); fprintf(stderr, " -f use force, removing existing file if it's " "not writeable\n"); fprintf(stderr, " -p preserve mode, ownership\n"); fprintf(stderr, " -r recursive copy\n"); fprintf(stderr, " -t preserve timestamps\n"); fprintf(stderr, " -u update only: don't copy if dest is newer\n"); fprintf(stderr, " -v verbose output (-vv is more verbose)\n"); return 2; } retVal = process(argc-optind, argv+optind, options); DBUG(("EXIT: %d\n", retVal)); return retVal; } ================================================ FILE: tools/apicheck/Android.bp ================================================ // Copyright (C) 2008 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], } java_binary_host { name: "apicheck", wrapper: "etc/apicheck", static_libs: [ "doclava", "jsilver", ], } ================================================ FILE: tools/apicheck/etc/apicheck ================================================ #!/bin/bash # # Copyright (C) 2005, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Set up prog to be the path of this script, including following symlinks, # and set up progdir to be the fully-qualified pathname of its directory. # # The classpath and other java options used in apicheck are specified in # build/make/core/tasks/apicheck.mk. prog="$0" while [ -h "${prog}" ]; do newProg=`/bin/ls -ld "${prog}"` newProg=`expr "${newProg}" : ".* -> \(.*\)$"` if expr "x${newProg}" : 'x/' >/dev/null; then prog="${newProg}" else progdir=`dirname "${prog}"` prog="${progdir}/${newProg}" fi done oldwd=`pwd` progdir=`dirname "${prog}"` cd "${progdir}" progdir=`pwd` prog="${progdir}"/`basename "${prog}"` cd "${oldwd}" javaOpts="" while expr "x$1" : 'x-J' >/dev/null; do opt=`expr "x$1" : 'x-J\(.*\)'` javaOpts="${javaOpts} -${opt}" shift done exec java $javaOpts com.google.doclava.apicheck.ApiCheck "$@" ================================================ FILE: tools/atree/Android.bp ================================================ // Copyright 2007 The Android Open Source Project // // Copies files into the directory structure described by a manifest package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } cc_binary_host { name: "atree", srcs: [ "atree.cpp", "files.cpp", "fs.cpp", ], cflags: ["-Wall", "-Werror"], static_libs: ["libhost"], } ================================================ FILE: tools/atree/atree.cpp ================================================ #include #include #include #include #include #include "options.h" #include "files.h" #include "fs.h" #include #include #include using namespace std; bool g_debug = getenv("ATREE_DEBUG") != NULL; vector g_listFiles; vector g_inputBases; map g_variables; string g_outputBase; string g_dependency; bool g_useHardLinks = false; const char* USAGE = "\n" "Usage: atree OPTIONS\n" "\n" "Options:\n" " -f FILELIST Specify one or more files containing the\n" " list of files to copy.\n" " -I INPUTDIR Specify one or more base directories in\n" " which to look for the files\n" " -o OUTPUTDIR Specify the directory to copy all of the\n" " output files to.\n" " -l Use hard links instead of copying the files.\n" " -m DEPENDENCY Output a make-formatted file containing the list.\n" " of files included. It sets the variable ATREE_FILES.\n" " -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n" " -d Verbose debug mode.\n" "\n" "FILELIST file format:\n" " The FILELIST files contain the list of files that will end up\n" " in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n" " directories in the order they are specified.\n" "\n" " In a FILELIST file, comment lines start with a #. Other lines\n" " are of the format:\n" "\n" " [rm|strip] DEST\n" " SRC [strip] DEST\n" " -SRCPATTERN\n" "\n" " DEST should be path relative to the output directory.\n" " 'rm DEST' removes the destination file and fails if it's missing.\n" " 'strip DEST' strips the binary destination file.\n" " If SRC is supplied, the file names can be different.\n" " SRCPATTERN is a pattern for the filenames.\n" "\n"; int usage() { fwrite(USAGE, strlen(USAGE), 1, stderr); return 1; } static bool add_variable(const char* arg) { const char* p = arg; while (*p && *p != '=') p++; if (*p == 0 || p == arg || p[1] == 0) { return false; } ostringstream var; var << "${" << string(arg, p-arg) << "}"; g_variables[var.str()] = string(p+1); return true; } static void debug_printf(const char* format, ...) { if (g_debug) { fflush(stderr); va_list ap; va_start(ap, format); vprintf(format, ap); va_end(ap); fflush(stdout); } } // Escape the filename so that it can be added to the makefile properly. static string escape_filename(const string& name) { ostringstream new_name; for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter) { switch (*iter) { case '$': new_name << "$$"; break; default: new_name << *iter; break; } } return new_name.str(); } int main(int argc, char* const* argv) { int err; bool done = false; while (!done) { int opt = getopt(argc, argv, "f:I:o:hlm:v:d"); switch (opt) { case -1: done = true; break; case 'f': g_listFiles.push_back(string(optarg)); break; case 'I': g_inputBases.push_back(string(optarg)); break; case 'o': if (g_outputBase.length() != 0) { fprintf(stderr, "%s: -o may only be supplied once -- " "-o %s\n", argv[0], optarg); return usage(); } g_outputBase = optarg; break; case 'l': g_useHardLinks = true; break; case 'm': if (g_dependency.length() != 0) { fprintf(stderr, "%s: -m may only be supplied once -- " "-m %s\n", argv[0], optarg); return usage(); } g_dependency = optarg; break; case 'v': if (!add_variable(optarg)) { fprintf(stderr, "%s Invalid expression in '-v %s': " "expected format is '-v VAR=VALUE'.\n", argv[0], optarg); return usage(); } break; case 'd': g_debug = true; break; default: case '?': case 'h': return usage(); } } if (optind != argc) { fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]); return usage(); } if (g_listFiles.size() == 0) { fprintf(stderr, "%s: At least one -f option must be supplied.\n", argv[0]); return usage(); } if (g_inputBases.size() == 0) { fprintf(stderr, "%s: At least one -I option must be supplied.\n", argv[0]); return usage(); } if (g_outputBase.length() == 0) { fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]); return usage(); } #if 0 for (vector::iterator it=g_listFiles.begin(); it!=g_listFiles.end(); it++) { printf("-f \"%s\"\n", it->c_str()); } for (vector::iterator it=g_inputBases.begin(); it!=g_inputBases.end(); it++) { printf("-I \"%s\"\n", it->c_str()); } printf("-o \"%s\"\n", g_outputBase.c_str()); if (g_useHardLinks) { printf("-l\n"); } #endif vector files; vector more; vector excludes; set directories; set deleted; // read file lists for (vector::iterator it=g_listFiles.begin(); it!=g_listFiles.end(); it++) { err = read_list_file(*it, g_variables, &files, &excludes); if (err != 0) { return err; } } // look for input files err = 0; for (vector::iterator it=files.begin(); it!=files.end(); it++) { err |= locate(&(*it), g_inputBases); } // expand the directories that we should copy into a list of files for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (it->sourceIsDir) { err |= list_dir(*it, excludes, &more); } } for (vector::iterator it=more.begin(); it!=more.end(); it++) { files.push_back(*it); } // get the name and modtime of the output files for (vector::iterator it=files.begin(); it!=files.end(); it++) { stat_out(g_outputBase, &(*it)); } if (err != 0) { return 1; } // gather directories for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (it->sourceIsDir) { directories.insert(it->outPath); } else { string s = dir_part(it->outPath); if (s != ".") { directories.insert(s); } } } // gather files that should become directores // and directories that should become files for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) { deleted.insert(it->outPath); } } // delete files for (set::iterator it=deleted.begin(); it!=deleted.end(); it++) { debug_printf("deleting %s\n", it->c_str()); err = remove_recursively(*it); if (err != 0) { return err; } } // remove all files or directories as requested from the input atree file. // must be done before create new directories. for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (!it->sourceIsDir) { if (it->fileOp == FILE_OP_REMOVE && deleted.count(it->outPath) == 0) { debug_printf("remove %s\n", it->outPath.c_str()); err = remove_recursively(it->outPath); if (err != 0) { return err; } } } } // make directories for (set::iterator it=directories.begin(); it!=directories.end(); it++) { debug_printf("mkdir %s\n", it->c_str()); err = mkdir_recursively(*it); if (err != 0) { return err; } } // copy (or link) files that are newer or of different size for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (!it->sourceIsDir) { if (it->fileOp == FILE_OP_REMOVE) { continue; } debug_printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(), it->sourceMod, it->outPath.c_str(), it->outMod); if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) { err = copy_file(it->sourcePath, it->outPath); debug_printf(" done.\n"); if (err != 0) { return err; } } else { debug_printf(" skipping.\n"); } if (it->fileOp == FILE_OP_STRIP) { debug_printf("strip %s\n", it->outPath.c_str()); err = strip_file(it->outPath); if (err != 0) { return err; } } } } // output the dependency file if (g_dependency.length() != 0) { FILE *f = fopen(g_dependency.c_str(), "w"); if (f != NULL) { fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n"); for (vector::iterator it=files.begin(); it!=files.end(); it++) { if (!it->sourceIsDir) { fprintf(f, "%s \\\n", escape_filename(it->sourcePath).c_str()); } } fprintf(f, "\n"); fclose(f); } else { fprintf(stderr, "error opening manifest file for write: %s\n", g_dependency.c_str()); } } return 0; } ================================================ FILE: tools/atree/files.cpp ================================================ #include "files.h" #include #include #include #include #include #include #include #include #include #include static bool is_comment_line(const char* p) { while (*p && isspace(*p)) { p++; } return *p == '#'; } static string path_append(const string& base, const string& leaf) { string full = base; if (base.length() > 0 && leaf.length() > 0) { full += '/'; } full += leaf; return full; } static bool is_whitespace_line(const char* p) { while (*p) { if (!isspace(*p)) { return false; } p++; } return true; } static bool is_exclude_line(const char* p) { while (*p) { if (*p == '-') { return true; } else if (isspace(*p)) { p++; } else { return false; } } return false; } void split_line(const char* p, vector* out) { const char* q = p; enum { WHITE, TEXT, IN_QUOTE } state = WHITE; while (*p) { if (*p == '#') { break; } switch (state) { case WHITE: if (!isspace(*p)) { q = p; state = (*p == '"') ? IN_QUOTE : TEXT; } break; case IN_QUOTE: if (*p == '"') { state = TEXT; break; } [[fallthrough]]; case TEXT: if (state != IN_QUOTE && isspace(*p)) { if (q != p) { const char* start = q; size_t len = p-q; if (len > 2 && *start == '"' && start[len - 1] == '"') { start++; len -= 2; } out->push_back(string(start, len)); } state = WHITE; } break; } p++; } if (state == TEXT) { const char* start = q; size_t len = p-q; if (len > 2 && *start == '"' && start[len - 1] == '"') { start++; len -= 2; } out->push_back(string(start, len)); } } static void add_file(vector* files, const FileOpType fileOp, const string& listFile, int listLine, const string& sourceName, const string& outName) { FileRecord rec; rec.listFile = listFile; rec.listLine = listLine; rec.fileOp = fileOp; rec.sourceName = sourceName; rec.outName = outName; files->push_back(rec); } static string replace_variables(const string& input, const map& variables, bool* error) { if (variables.empty()) { return input; } // Abort if the variable prefix is not found if (input.find("${") == string::npos) { return input; } string result = input; // Note: rather than be fancy to detect recursive replacements, // we simply iterate till a given threshold is met. int retries = 1000; bool did_replace; do { did_replace = false; for (map::const_iterator it = variables.begin(); it != variables.end(); ++it) { string::size_type pos = 0; while((pos = result.find(it->first, pos)) != string::npos) { result = result.replace(pos, it->first.length(), it->second); pos += it->second.length(); did_replace = true; } } if (did_replace && --retries == 0) { *error = true; fprintf(stderr, "Recursive replacement detected during variables " "substitution. Full list of variables is: "); for (map::const_iterator it = variables.begin(); it != variables.end(); ++it) { fprintf(stderr, " %s=%s\n", it->first.c_str(), it->second.c_str()); } return result; } } while (did_replace); return result; } int read_list_file(const string& filename, const map& variables, vector* files, vector* excludes) { int err = 0; FILE* f = NULL; long size; char* buf = NULL; char *p, *q; int i, lineCount; f = fopen(filename.c_str(), "r"); if (f == NULL) { fprintf(stderr, "Could not open list file (%s): %s\n", filename.c_str(), strerror(errno)); err = errno; goto cleanup; } err = fseek(f, 0, SEEK_END); if (err != 0) { fprintf(stderr, "Could not seek to the end of file %s. (%s)\n", filename.c_str(), strerror(errno)); err = errno; goto cleanup; } size = ftell(f); err = fseek(f, 0, SEEK_SET); if (err != 0) { fprintf(stderr, "Could not seek to the beginning of file %s. (%s)\n", filename.c_str(), strerror(errno)); err = errno; goto cleanup; } buf = (char*)malloc(size+1); if (buf == NULL) { // (potentially large) fprintf(stderr, "out of memory (%ld)\n", size); err = ENOMEM; goto cleanup; } if (1 != fread(buf, size, 1, f)) { fprintf(stderr, "error reading file %s. (%s)\n", filename.c_str(), strerror(errno)); err = errno; goto cleanup; } // split on lines p = buf; q = buf+size; lineCount = 0; while (ppush_back(string(p)); } else { vector words; split_line(p, &words); #if 0 printf("[ "); for (size_t k=0; k::iterator it = words.begin(); it != words.end(); ++it) { const string& word = *it; if (word == "rm") { if (op != FILE_OP_COPY) { errstr = "Error: you can only specifiy 'rm' or 'strip' once per line."; break; } op = FILE_OP_REMOVE; } else if (word == "strip") { if (op != FILE_OP_COPY) { errstr = "Error: you can only specifiy 'rm' or 'strip' once per line."; break; } op = FILE_OP_STRIP; } else if (pcount < 2) { bool error = false; paths[pcount++] = replace_variables(word, variables, &error); if (error) { err = 1; goto cleanup; } } else { errstr = "Error: More than 2 paths per line."; break; } } if (pcount == 0 && !errstr.empty()) { errstr = "Error: No path found on line."; } if (!errstr.empty()) { fprintf(stderr, "%s:%d: bad format: %s\n%s\nExpected: [SRC] [rm|strip] DEST\n", filename.c_str(), i+1, p, errstr.c_str()); err = 1; } else { if (pcount == 1) { // pattern: [rm|strip] DEST paths[1] = paths[0]; } add_file(files, op, filename, i+1, paths[0], paths[1]); } } p = q; } cleanup: if (buf != NULL) { free(buf); } if (f != NULL) { fclose(f); } return err; } int locate(FileRecord* rec, const vector& search) { if (rec->fileOp == FILE_OP_REMOVE) { // Don't touch source files when removing a destination. rec->sourceMod = 0; rec->sourceSize = 0; rec->sourceIsDir = false; return 0; } int err; for (vector::const_iterator it=search.begin(); it!=search.end(); it++) { string full = path_append(*it, rec->sourceName); struct stat st; err = stat(full.c_str(), &st); if (err == 0) { rec->sourceBase = *it; rec->sourcePath = full; rec->sourceMod = st.st_mtime; rec->sourceSize = st.st_size; rec->sourceIsDir = S_ISDIR(st.st_mode); return 0; } } fprintf(stderr, "%s:%d: couldn't locate source file: %s\n", rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str()); return 1; } void stat_out(const string& base, FileRecord* rec) { rec->outPath = path_append(base, rec->outName); int err; struct stat st; err = stat(rec->outPath.c_str(), &st); if (err == 0) { rec->outMod = st.st_mtime; rec->outSize = st.st_size; rec->outIsDir = S_ISDIR(st.st_mode); } else { rec->outMod = 0; rec->outSize = 0; rec->outIsDir = false; } } string dir_part(const string& filename) { int pos = filename.rfind('/'); if (pos <= 0) { return "."; } return filename.substr(0, pos); } static void add_more(const string& entry, bool isDir, const FileRecord& rec, vector*more) { FileRecord r; r.listFile = rec.listFile; r.listLine = rec.listLine; r.sourceName = path_append(rec.sourceName, entry); r.sourcePath = path_append(rec.sourceBase, r.sourceName); struct stat st; int err = stat(r.sourcePath.c_str(), &st); if (err == 0) { r.sourceMod = st.st_mtime; } r.sourceIsDir = isDir; r.outName = path_append(rec.outName, entry); more->push_back(r); } static bool matches_excludes(const char* file, const vector& excludes) { for (vector::const_iterator it=excludes.begin(); it!=excludes.end(); it++) { if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) { return true; } } return false; } static int list_dir(const string& path, const FileRecord& rec, const vector& excludes, vector* more) { string full = path_append(rec.sourceBase, rec.sourceName); full = path_append(full, path); DIR *d = opendir(full.c_str()); if (d == NULL) { return errno; } vector dirs; struct dirent *ent; while (NULL != (ent = readdir(d))) { if (0 == strcmp(".", ent->d_name) || 0 == strcmp("..", ent->d_name)) { continue; } if (matches_excludes(ent->d_name, excludes)) { continue; } string entry = path_append(path, ent->d_name); bool is_directory = (ent->d_type == DT_DIR); add_more(entry, is_directory, rec, more); if (is_directory) { dirs.push_back(entry); } } closedir(d); for (vector::iterator it=dirs.begin(); it!=dirs.end(); it++) { list_dir(*it, rec, excludes, more); } return 0; } int list_dir(const FileRecord& rec, const vector& excludes, vector* files) { return list_dir("", rec, excludes, files); } FileRecord::FileRecord() { fileOp = FILE_OP_COPY; } ================================================ FILE: tools/atree/files.h ================================================ #ifndef FILES_H #define FILES_H #include #include #include #include using namespace std; enum FileOpType { FILE_OP_COPY = 0, FILE_OP_REMOVE, FILE_OP_STRIP }; struct FileRecord { FileRecord(); string listFile; int listLine; string sourceBase; string sourceName; string sourcePath; bool sourceIsDir; time_t sourceMod; off_t sourceSize; FileOpType fileOp; string outName; string outPath; off_t outSize; time_t outMod; bool outIsDir; unsigned int mode; }; int read_list_file(const string& filename, const map& variables, vector* files, vector* excludes); int locate(FileRecord* rec, const vector& search); void stat_out(const string& base, FileRecord* rec); string dir_part(const string& filename); int list_dir(const FileRecord& rec, const vector& excludes, vector* files); #endif // FILES_H ================================================ FILE: tools/atree/fs.cpp ================================================ #include "fs.h" #include "files.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; static bool is_dir(const string& path) { int err; struct stat st; err = stat(path.c_str(), &st); return err != 0 || S_ISDIR(st.st_mode); } static int remove_file(const string& path) { int err = unlink(path.c_str()); if (err != 0) { fprintf(stderr, "error deleting file %s (%s)\n", path.c_str(), strerror(errno)); return errno; } return 0; } int remove_recursively(const string& path) { int err; if (is_dir(path)) { DIR *d = opendir(path.c_str()); if (d == NULL) { fprintf(stderr, "error getting directory contents %s (%s)\n", path.c_str(), strerror(errno)); return errno; } vector files; vector dirs; struct dirent *ent; while (NULL != (ent = readdir(d))) { if (0 == strcmp(".", ent->d_name) || 0 == strcmp("..", ent->d_name)) { continue; } string full = path; full += '/'; full += ent->d_name; bool is_directory = (ent->d_type == DT_DIR); if (is_directory) { dirs.push_back(full); } else { files.push_back(full); } } closedir(d); for (vector::iterator it=files.begin(); it!=files.end(); it++) { err = remove_file(*it); if (err != 0) { return err; } } for (vector::iterator it=dirs.begin(); it!=dirs.end(); it++) { err = remove_recursively(*it); if (err != 0) { return err; } } err = rmdir(path.c_str()); if (err != 0) { fprintf(stderr, "error deleting directory %s (%s)\n", path.c_str(), strerror(errno)); return errno; } return 0; } else { return remove_file(path); } } int mkdir_recursively(const string& path) { int err; size_t pos = 0; // For absolute pathnames, that starts with leading '/' // use appropriate initial value. if (path.length() != 0 and path[0] == '/') pos++; while (true) { pos = path.find('/', pos); string p = path.substr(0, pos); struct stat st; err = stat(p.c_str(), &st); if (err != 0) { err = mkdir(p.c_str(), 0770); if (err != 0) { fprintf(stderr, "can't create directory %s (%s)\n", path.c_str(), strerror(errno)); return errno; } } else if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "can't create directory %s because %s is a file.\n", path.c_str(), p.c_str()); return 1; } pos++; if (p == path) { return 0; } } } int copy_file(const string& src, const string& dst) { int err; err = copyFile(src.c_str(), dst.c_str(), COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS); return err; } int strip_file(const string& path) { // Default strip command to run is "strip" unless overridden by the ATREE_STRIP env var. const char* strip_cmd = getenv("ATREE_STRIP"); if (!strip_cmd || !strip_cmd[0]) { strip_cmd = "strip"; } pid_t pid = fork(); if (pid == -1) { // Fork failed. errno should be set. return -1; } else if (pid == 0) { // Exec in the child. Only returns if execve failed. int num_args = 0; const char *s = strip_cmd; while (*s) { while (*s == ' ') ++s; if (*s && *s != ' ') { ++num_args; while (*s && *s != ' ') ++s; } } if (num_args <= 0) { fprintf(stderr, "Invalid ATREE_STRIP command '%s'\n", strip_cmd); return 1; } else if (num_args == 1) { return execlp(strip_cmd, strip_cmd, path.c_str(), (char *)NULL); } else { // Split the arguments if more than 1 char* cmd = strdup(strip_cmd); const char** args = (const char**) calloc((num_args + 2), sizeof(const char*)); const char** curr = args; char* s = cmd; while (*s) { while (*s == ' ') ++s; if (*s && *s != ' ') { *curr = s; ++curr; while (*s && *s != ' ') ++s; if (*s) { *s = '\0'; ++s; } } } args[num_args] = path.c_str(); args[num_args + 1] = NULL; int ret = execvp(args[0], (char* const*)args); free(args); free(cmd); return ret; } } else { // Wait for child pid and return its exit code. int status; waitpid(pid, &status, 0); return status; } } ================================================ FILE: tools/atree/fs.h ================================================ #ifndef FS_H #define FS_H #include using namespace std; int remove_recursively(const string& path); int mkdir_recursively(const string& path); int copy_file(const string& src, const string& dst); int strip_file(const string& path); #endif // FS_H ================================================ FILE: tools/atree/options.h ================================================ #ifndef OPTIONS_H #define OPTIONS_H #include #include using namespace std; extern vector g_listFiles; extern vector g_inputBases; extern string g_outputBase; extern bool g_useHardLinks; #endif // OPTIONS_H ================================================ FILE: tools/auto_gen_test_config.py ================================================ #!/usr/bin/env python3 # # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A tool to generate TradeFed test config file. """ import argparse import re import os import shutil import sys from xml.dom.minidom import parse ATTRIBUTE_LABEL = 'android:label' ATTRIBUTE_RUNNER = 'android:name' ATTRIBUTE_PACKAGE = 'package' PLACEHOLDER_LABEL = '{LABEL}' PLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}' PLACEHOLDER_MODULE = '{MODULE}' PLACEHOLDER_PACKAGE = '{PACKAGE}' PLACEHOLDER_RUNNER = '{RUNNER}' PLACEHOLDER_TEST_TYPE = '{TEST_TYPE}' PLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS = '{EXTRA_TEST_RUNNER_CONFIGS}' def main(argv): """Entry point of auto_gen_test_config. Args: argv: A list of arguments. Returns: 0 if no error, otherwise 1. """ parser = argparse.ArgumentParser() parser.add_argument( "target_config", help="Path to the generated output config.") parser.add_argument( "android_manifest", help="Path to AndroidManifest.xml or output of 'aapt2 dump xmltree' with .xmltree extension.") parser.add_argument( "empty_config", help="Path to the empty config template.") parser.add_argument( "instrumentation_test_config_template", help="Path to the instrumentation test config template.") parser.add_argument("--extra-configs", default="") parser.add_argument("--extra-test-runner-configs", default="") args = parser.parse_args(argv) target_config = args.target_config android_manifest = args.android_manifest empty_config = args.empty_config instrumentation_test_config_template = args.instrumentation_test_config_template extra_configs = '\n'.join(args.extra_configs.split('\\n')) extra_test_runner_configs = '\n'.join(args.extra_test_runner_configs.split('\\n')) module = os.path.splitext(os.path.basename(target_config))[0] # If the AndroidManifest.xml is not available, but the APK is, this tool also # accepts the output of `aapt2 dump xmltree AndroidManifest.xml` written # into a file. This is a custom structured aapt2 output - not raw XML! if android_manifest.endswith(".xmltree"): label = module with open(android_manifest, encoding="utf-8") as manifest: # e.g. A: package="android.test.example.helloworld" (Raw: "android.test.example.helloworld") # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern = re.compile(r"\(Raw:\s\"(.*)\"\)$") curr_element = None for line in manifest: curr_line = line.strip() if curr_line.startswith("E:"): # e.g. "E: instrumentation (line=9)" # ^^^^^^^^^^^^^^^ curr_element = curr_line.split(" ")[1] if curr_element == "instrumentation": if ATTRIBUTE_RUNNER in curr_line: runner = re.findall(pattern, curr_line)[0] if ATTRIBUTE_LABEL in curr_line: label = re.findall(pattern, curr_line)[0] if curr_element == "manifest": if ATTRIBUTE_PACKAGE in curr_line: package = re.findall(pattern, curr_line)[0] if not (runner and label and package): # Failed to locate instrumentation or manifest element in AndroidManifest. # file. Empty test config file will be created. shutil.copyfile(empty_config, target_config) return 0 else: # If the AndroidManifest.xml file is directly available, read it as an XML file. manifest = parse(android_manifest) instrumentation_elements = manifest.getElementsByTagName('instrumentation') manifest_elements = manifest.getElementsByTagName('manifest') if len(instrumentation_elements) != 1 or len(manifest_elements) != 1: # Failed to locate instrumentation or manifest element in AndroidManifest. # file. Empty test config file will be created. shutil.copyfile(empty_config, target_config) return 0 instrumentation = instrumentation_elements[0] manifest = manifest_elements[0] if ATTRIBUTE_LABEL in instrumentation.attributes: label = instrumentation.attributes[ATTRIBUTE_LABEL].value else: label = module runner = instrumentation.attributes[ATTRIBUTE_RUNNER].value package = manifest.attributes[ATTRIBUTE_PACKAGE].value test_type = ('InstrumentationTest' if runner.endswith('.InstrumentationTestRunner') else 'AndroidJUnitTest') with open(instrumentation_test_config_template) as template: config = template.read() config = config.replace(PLACEHOLDER_LABEL, label) config = config.replace(PLACEHOLDER_MODULE, module) config = config.replace(PLACEHOLDER_PACKAGE, package) config = config.replace(PLACEHOLDER_TEST_TYPE, test_type) config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs) config = config.replace(PLACEHOLDER_EXTRA_TEST_RUNNER_CONFIGS, extra_test_runner_configs) config = config.replace(PLACEHOLDER_RUNNER, runner) with open(target_config, 'w') as config_file: config_file.write(config) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) ================================================ FILE: tools/auto_gen_test_config_test.py ================================================ #!/usr/bin/env python # # Copyright 2017, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unittests for auto_gen_test_config.""" import os import shutil import tempfile import unittest import auto_gen_test_config TEST_MODULE = 'TestModule' MANIFEST_INVALID = """ """ XMLTREE_JUNIT_TEST = """N: android=http://schemas.android.com/apk/res/android (line=2) E: manifest (line=2) A: package="com.android.my.tests.x" (Raw: "com.android.my.tests.x") E: instrumentation (line=9) A: http://schemas.android.com/apk/res/android:label(0x01010001)="TestModule" (Raw: "TestModule") A: http://schemas.android.com/apk/res/android:name(0x01010003)="androidx.test.runner.AndroidJUnitRunner" (Raw: "androidx.test.runner.AndroidJUnitRunner") A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="com.android.my.tests" (Raw: "com.android.my.tests") """ XMLTREE_INSTRUMENTATION_TEST = """N: android=http://schemas.android.com/apk/res/android (line=2) E: manifest (line=2) A: package="com.android.my.tests.x" (Raw: "com.android.my.tests.x") E: instrumentation (line=9) A: http://schemas.android.com/apk/res/android:label(0x01010001)="TestModule" (Raw: "TestModule") A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.test.InstrumentationTestRunner" (Raw: "android.test.InstrumentationTestRunner") A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="com.android.my.tests" (Raw: "com.android.my.tests") """ MANIFEST_JUNIT_TEST = """ """ MANIFEST_INSTRUMENTATION_TEST = """ """ EXPECTED_JUNIT_TEST_CONFIG = """ """ EXPECTED_INSTRUMENTATION_TEST_CONFIG = """ """ EMPTY_TEST_CONFIG_CONTENT = """ """ INSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT = """ """ class AutoGenTestConfigUnittests(unittest.TestCase): """Unittests for auto_gen_test_config.""" def setUp(self): """Setup directory for test.""" self.test_dir = tempfile.mkdtemp() self.config_file = os.path.join(self.test_dir, TEST_MODULE + '.config') self.manifest_file = os.path.join(self.test_dir, 'AndroidManifest.xml') self.xmltree_file = os.path.join(self.test_dir, TEST_MODULE + '.xmltree') self.empty_test_config_file = os.path.join(self.test_dir, 'empty.config') self.instrumentation_test_config_template_file = os.path.join( self.test_dir, 'instrumentation.config') with open(self.empty_test_config_file, 'w') as f: f.write(EMPTY_TEST_CONFIG_CONTENT) with open(self.instrumentation_test_config_template_file, 'w') as f: f.write(INSTRUMENTATION_TEST_CONFIG_TEMPLATE_CONTENT) def tearDown(self): """Cleanup the test directory.""" shutil.rmtree(self.test_dir, ignore_errors=True) def testInvalidManifest(self): """An empty test config should be generated if AndroidManifest is invalid. """ with open(self.manifest_file, 'w') as f: f.write(MANIFEST_INVALID) argv = [self.config_file, self.manifest_file, self.empty_test_config_file, self.instrumentation_test_config_template_file] auto_gen_test_config.main(argv) with open(self.config_file) as config_file: with open(self.empty_test_config_file) as empty_config: self.assertEqual(config_file.read(), empty_config.read()) def testCreateJUnitTestConfig(self): """Test creating test config for AndroidJUnitTest. """ with open(self.manifest_file, 'w') as f: f.write(MANIFEST_JUNIT_TEST) argv = [self.config_file, self.manifest_file, self.empty_test_config_file, self.instrumentation_test_config_template_file] auto_gen_test_config.main(argv) with open(self.config_file) as config_file: self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG) def testCreateInstrumentationTestConfig(self): """Test creating test config for InstrumentationTest. """ with open(self.manifest_file, 'w') as f: f.write(MANIFEST_INSTRUMENTATION_TEST) argv = [self.config_file, self.manifest_file, self.empty_test_config_file, self.instrumentation_test_config_template_file] auto_gen_test_config.main(argv) with open(self.config_file) as config_file: self.assertEqual( config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG) def testCreateJUnitTestConfigWithXMLTree(self): """Test creating test config for AndroidJUnitTest. """ with open(self.xmltree_file, 'w') as f: f.write(XMLTREE_JUNIT_TEST) argv = [self.config_file, self.xmltree_file, self.empty_test_config_file, self.instrumentation_test_config_template_file] auto_gen_test_config.main(argv) with open(self.config_file) as config_file: self.assertEqual(config_file.read(), EXPECTED_JUNIT_TEST_CONFIG) def testCreateInstrumentationTestConfigWithXMLTree(self): """Test creating test config for InstrumentationTest. """ with open(self.xmltree_file, 'w') as f: f.write(XMLTREE_INSTRUMENTATION_TEST) argv = [self.config_file, self.xmltree_file, self.empty_test_config_file, self.instrumentation_test_config_template_file] auto_gen_test_config.main(argv) with open(self.config_file) as config_file: self.assertEqual( config_file.read(), EXPECTED_INSTRUMENTATION_TEST_CONFIG) if __name__ == '__main__': unittest.main() ================================================ FILE: tools/brillo-clang-format ================================================ # # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ### DO NOT COPY THIS FILE TO YOUR PROJECT. ### # # This is the .clang-format file used by all Brillo projects, conforming to the # style guide defined by Brillo. To use this file create a *relative* symlink in # your project pointing to this file, as this repository is expected to be # present in all manifests. # # See go/brillo-c++-style for details about the style guide. # BasedOnStyle: Google AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false BinPackArguments: false BinPackParameters: false CommentPragmas: NOLINT:.* DerivePointerAlignment: false PointerAlignment: Left TabWidth: 2 ================================================ FILE: tools/build-runfiles.cc ================================================ // Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This program creates a "runfiles tree" from a "runfiles manifest". // // The command line arguments are an input manifest INPUT and an output // directory RUNFILES. First, the files in the RUNFILES directory are scanned // and any extraneous ones are removed. Second, any missing files are created. // Finally, a copy of the input manifest is written to RUNFILES/MANIFEST. // // The input manifest consists of lines, each containing a relative path within // the runfiles, a space, and an optional absolute path. If this second path // is present, a symlink is created pointing to it; otherwise an empty file is // created. // // Given the line // /output/path /real/path // we will create directories // RUNFILES/ // RUNFILES//output // a symlink // RUNFILES//output/path -> /real/path // and the output manifest will contain a line // /output/path /real/path // // If --use_metadata is supplied, every other line is treated as opaque // metadata, and is ignored here. // // All output paths must be relative and generally (but not always) begin with // . No output path may be equal to another. No output path may // be a path prefix of another. #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include // program_invocation_short_name is not portable. static const char *argv0; const char *input_filename; const char *output_base_dir; enum FileType { FILE_TYPE_REGULAR, FILE_TYPE_DIRECTORY, FILE_TYPE_SYMLINK }; struct FileInfo { FileType type; std::string symlink_target; bool operator==(const FileInfo &other) const { return type == other.type && symlink_target == other.symlink_target; } bool operator!=(const FileInfo &other) const { return !(*this == other); } }; typedef std::map FileInfoMap; class RunfilesCreator { public: explicit RunfilesCreator(const std::string &output_base) : output_base_(output_base), output_filename_("MANIFEST"), temp_filename_(output_filename_ + ".tmp") { SetupOutputBase(); if (chdir(output_base_.c_str()) != 0) { err(2, "chdir '%s'", output_base_.c_str()); } } void ReadManifest(const std::string &manifest_file, bool allow_relative, bool use_metadata) { FILE *outfile = fopen(temp_filename_.c_str(), "w"); if (!outfile) { err(2, "opening '%s/%s' for writing", output_base_.c_str(), temp_filename_.c_str()); } FILE *infile = fopen(manifest_file.c_str(), "r"); if (!infile) { err(2, "opening '%s' for reading", manifest_file.c_str()); } // read input manifest int lineno = 0; char buf[3 * PATH_MAX]; while (fgets(buf, sizeof buf, infile)) { // copy line to output manifest if (fputs(buf, outfile) == EOF) { err(2, "writing to '%s/%s'", output_base_.c_str(), temp_filename_.c_str()); } // parse line ++lineno; // Skip metadata lines. They are used solely for // dependency checking. if (use_metadata && lineno % 2 == 0) continue; char *tok = strtok(buf, " \n"); if (tok == nullptr) { continue; } else if (*tok == '/') { errx(2, "%s:%d: paths must not be absolute", input_filename, lineno); } std::string link(tok); const char *target = strtok(nullptr, " \n"); if (target == nullptr) { target = ""; } else if (strtok(nullptr, " \n") != nullptr) { errx(2, "%s:%d: link or target filename contains space", input_filename, lineno); } else if (!allow_relative && target[0] != '/') { errx(2, "%s:%d: expected absolute path", input_filename, lineno); } FileInfo *info = &manifest_[link]; if (target[0] == '\0') { // No target means an empty file. info->type = FILE_TYPE_REGULAR; } else { info->type = FILE_TYPE_SYMLINK; info->symlink_target = target; } FileInfo parent_info; parent_info.type = FILE_TYPE_DIRECTORY; while (true) { int k = link.rfind('/'); if (k < 0) break; link.erase(k, std::string::npos); if (!manifest_.insert(std::make_pair(link, parent_info)).second) break; } } if (fclose(outfile) != 0) { err(2, "writing to '%s/%s'", output_base_.c_str(), temp_filename_.c_str()); } fclose(infile); // Don't delete the temp manifest file. manifest_[temp_filename_].type = FILE_TYPE_REGULAR; } void CreateRunfiles() { if (unlink(output_filename_.c_str()) != 0 && errno != ENOENT) { err(2, "removing previous file at '%s/%s'", output_base_.c_str(), output_filename_.c_str()); } ScanTreeAndPrune("."); CreateFiles(); // rename output file into place if (rename(temp_filename_.c_str(), output_filename_.c_str()) != 0) { err(2, "renaming '%s/%s' to '%s/%s'", output_base_.c_str(), temp_filename_.c_str(), output_base_.c_str(), output_filename_.c_str()); } } private: void SetupOutputBase() { struct stat st; if (stat(output_base_.c_str(), &st) != 0) { // Technically, this will cause problems if the user's umask contains // 0200, but we don't care. Anyone who does that deserves what's coming. if (mkdir(output_base_.c_str(), 0777) != 0) { err(2, "creating directory '%s'", output_base_.c_str()); } } else { EnsureDirReadAndWritePerms(output_base_); } } void ScanTreeAndPrune(const std::string &path) { // A note on non-empty files: // We don't distinguish between empty and non-empty files. That is, if // there's a file that has contents, we don't truncate it here, even though // the manifest supports creation of empty files, only. Given that // .runfiles are *supposed* to be immutable, this shouldn't be a problem. EnsureDirReadAndWritePerms(path); struct dirent *entry; DIR *dh = opendir(path.c_str()); if (!dh) { err(2, "opendir '%s'", path.c_str()); } errno = 0; const std::string prefix = (path == "." ? "" : path + "/"); while ((entry = readdir(dh)) != nullptr) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; std::string entry_path = prefix + entry->d_name; FileInfo actual_info; actual_info.type = DentryToFileType(entry_path, entry); if (actual_info.type == FILE_TYPE_SYMLINK) { ReadLinkOrDie(entry_path, &actual_info.symlink_target); } FileInfoMap::iterator expected_it = manifest_.find(entry_path); if (expected_it == manifest_.end() || expected_it->second != actual_info) { DelTree(entry_path, actual_info.type); } else { manifest_.erase(expected_it); if (actual_info.type == FILE_TYPE_DIRECTORY) { ScanTreeAndPrune(entry_path); } } errno = 0; } if (errno != 0) { err(2, "reading directory '%s'", path.c_str()); } closedir(dh); } void CreateFiles() { for (FileInfoMap::const_iterator it = manifest_.begin(); it != manifest_.end(); ++it) { const std::string &path = it->first; switch (it->second.type) { case FILE_TYPE_DIRECTORY: if (mkdir(path.c_str(), 0777) != 0) { err(2, "mkdir '%s'", path.c_str()); } break; case FILE_TYPE_REGULAR: { int fd = open(path.c_str(), O_CREAT|O_EXCL|O_WRONLY, 0555); if (fd < 0) { err(2, "creating empty file '%s'", path.c_str()); } close(fd); } break; case FILE_TYPE_SYMLINK: { const std::string& target = it->second.symlink_target; if (symlink(target.c_str(), path.c_str()) != 0) { err(2, "symlinking '%s' -> '%s'", path.c_str(), target.c_str()); } } break; } } } FileType DentryToFileType(const std::string &path, struct dirent *ent) { #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN) { if (ent->d_type == DT_DIR) { return FILE_TYPE_DIRECTORY; } else if (ent->d_type == DT_LNK) { return FILE_TYPE_SYMLINK; } else { return FILE_TYPE_REGULAR; } } else // NOLINT (the brace is in the next line) #endif { struct stat st; LStatOrDie(path, &st); if (S_ISDIR(st.st_mode)) { return FILE_TYPE_DIRECTORY; } else if (S_ISLNK(st.st_mode)) { return FILE_TYPE_SYMLINK; } else { return FILE_TYPE_REGULAR; } } } void LStatOrDie(const std::string &path, struct stat *st) { if (lstat(path.c_str(), st) != 0) { err(2, "lstating file '%s'", path.c_str()); } } void StatOrDie(const std::string &path, struct stat *st) { if (stat(path.c_str(), st) != 0) { err(2, "stating file '%s'", path.c_str()); } } void ReadLinkOrDie(const std::string &path, std::string *output) { char readlink_buffer[PATH_MAX]; int sz = readlink(path.c_str(), readlink_buffer, sizeof(readlink_buffer)); if (sz < 0) { err(2, "reading symlink '%s'", path.c_str()); } // readlink returns a non-null terminated string. std::string(readlink_buffer, sz).swap(*output); } void EnsureDirReadAndWritePerms(const std::string &path) { const int kMode = 0700; struct stat st; LStatOrDie(path, &st); if ((st.st_mode & kMode) != kMode) { int new_mode = st.st_mode | kMode; if (chmod(path.c_str(), new_mode) != 0) { err(2, "chmod '%s'", path.c_str()); } } } bool DelTree(const std::string &path, FileType file_type) { if (file_type != FILE_TYPE_DIRECTORY) { if (unlink(path.c_str()) != 0) { err(2, "unlinking '%s'", path.c_str()); return false; } return true; } EnsureDirReadAndWritePerms(path); struct dirent *entry; DIR *dh = opendir(path.c_str()); if (!dh) { err(2, "opendir '%s'", path.c_str()); } errno = 0; while ((entry = readdir(dh)) != nullptr) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; const std::string entry_path = path + '/' + entry->d_name; FileType entry_file_type = DentryToFileType(entry_path, entry); DelTree(entry_path, entry_file_type); errno = 0; } if (errno != 0) { err(2, "readdir '%s'", path.c_str()); } closedir(dh); if (rmdir(path.c_str()) != 0) { err(2, "rmdir '%s'", path.c_str()); } return true; } private: std::string output_base_; std::string output_filename_; std::string temp_filename_; FileInfoMap manifest_; }; int main(int argc, char **argv) { argv0 = argv[0]; argc--; argv++; bool allow_relative = false; bool use_metadata = false; while (argc >= 1) { if (strcmp(argv[0], "--allow_relative") == 0) { allow_relative = true; argc--; argv++; } else if (strcmp(argv[0], "--use_metadata") == 0) { use_metadata = true; argc--; argv++; } else { break; } } if (argc != 2) { fprintf(stderr, "usage: %s " "[--allow_relative] [--use_metadata] " "INPUT RUNFILES\n", argv0); return 1; } input_filename = argv[0]; output_base_dir = argv[1]; std::string manifest_file = input_filename; if (input_filename[0] != '/') { char cwd_buf[PATH_MAX]; if (getcwd(cwd_buf, sizeof(cwd_buf)) == nullptr) { err(2, "getcwd failed"); } manifest_file = std::string(cwd_buf) + '/' + manifest_file; } RunfilesCreator runfiles_creator(output_base_dir); runfiles_creator.ReadManifest(manifest_file, allow_relative, use_metadata); runfiles_creator.CreateRunfiles(); return 0; } ================================================ FILE: tools/canoninja/README.md ================================================ # Ninja File Canonicalizer Suppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during the testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of lines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the rule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja. Canoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a simple `comm` command immediately reveal the essential difference between the files. ## Example Consider the following makefile ```makefile second := first: foo foo: @echo foo second: bar bar: @echo bar ``` Depending on Kati version converting it to Ninja file will yield either: ``` $ cat /tmp/1.ninja # Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb pool local_pool depth = 72 build _kati_always_build_: phony build first: phony foo rule rule0 description = build $out command = /bin/sh -c "echo foo" build foo: rule0 build second: phony bar rule rule1 description = build $out command = /bin/sh -c "echo bar" build bar: rule1 default first ``` or ``` $ cat 2.ninja # Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310 pool local_pool depth = 72 build _kati_always_build_: phony build second: phony bar rule rule0 description = build $out command = /bin/sh -c "echo bar" build bar: rule0 build first: phony foo rule rule1 description = build $out command = /bin/sh -c "echo foo" build foo: rule1 default first ``` This is a quirk in Kati, see https://github.com/google/kati/issues/238 Trying to find out the difference between the targets even after sorting them isn't too helpful: ``` diff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort) 1c1 < build bar: rule1 --- > build bar: rule0 3c3 < build foo: rule0 --- > build foo: rule1 ``` However, running these files through `canoninja` yields ``` $ canoninja /tmp/1.ninja # Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb pool local_pool depth = 72 build _kati_always_build_: phony build first: phony foo rule R2f9981d3c152fc255370dc67028244f7bed72a03 description = build $out command = /bin/sh -c "echo foo" build foo: R2f9981d3c152fc255370dc67028244f7bed72a03 build second: phony bar rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c description = build $out command = /bin/sh -c "echo bar" build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c default first ``` and ``` ~/go/bin/canoninja /tmp/2.ninja # Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310 pool local_pool depth = 72 build _kati_always_build_: phony build second: phony bar rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c description = build $out command = /bin/sh -c "echo bar" build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c build first: phony foo rule R2f9981d3c152fc255370dc67028244f7bed72a03 description = build $out command = /bin/sh -c "echo foo" build foo: R2f9981d3c152fc255370dc67028244f7bed72a03 default first ``` and when we extract only build statements and sort them, we see that both Ninja files define the same graph: ```shell $ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \ <(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort) ``` # Todo * Optionally output only the build statements, optionally sorted * Handle continuation lines correctly ================================================ FILE: tools/canoninja/canoninja.go ================================================ package canoninja import ( "bytes" "crypto/sha1" "encoding/hex" "fmt" "io" ) var ( rulePrefix = []byte("rule ") buildPrefix = []byte("build ") phonyRule = []byte("phony") ) func Generate(path string, buffer []byte, sink io.Writer) error { // Break file into lines from := 0 var lines [][]byte for from < len(buffer) { line := getLine(buffer[from:]) lines = append(lines, line) from += len(line) } // FOr each rule, calculate and remember its digest ruleDigest := make(map[string]string) for i := 0; i < len(lines); { if bytes.HasPrefix(lines[i], rulePrefix) { // Find ruleName rn := ruleName(lines[i]) if len(rn) == 0 { return fmt.Errorf("%s:%d: rule name is missing or on the next line", path, i+1) } sRuleName := string(rn) if _, ok := ruleDigest[sRuleName]; ok { return fmt.Errorf("%s:%d: the rule %s has been already defined", path, i+1, sRuleName) } // Calculate rule text digest as a digests of line digests. var digests []byte doDigest := func(b []byte) { h := sha1.New() h.Write(b) digests = h.Sum(digests) } // For the first line, digest everything after rule's name doDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):]) for i++; i < len(lines) && lines[i][0] == ' '; i++ { doDigest(lines[i]) } h := sha1.New() h.Write(digests) ruleDigest[sRuleName] = "R" + hex.EncodeToString(h.Sum(nil)) } else { i++ } } // Rewrite rule names. for i, line := range lines { if bytes.HasPrefix(line, buildPrefix) { brn := getBuildRuleName(line) if bytes.Equal(brn, phonyRule) { sink.Write(line) continue } if len(brn) == 0 { return fmt.Errorf("%s:%d: build statement lacks rule name", path, i+1) } sink.Write(line[0 : cap(line)-cap(brn)]) if digest, ok := ruleDigest[string(brn)]; ok { sink.Write([]byte(digest)) } else { return fmt.Errorf("%s:%d: no rule for this build target", path, i+1) } sink.Write(line[cap(line)+len(brn)-cap(brn):]) } else if bytes.HasPrefix(line, rulePrefix) { rn := ruleName(line) // Write everything before it sink.Write(line[0 : cap(line)-cap(rn)]) sink.Write([]byte(ruleDigest[string(rn)])) sink.Write(line[cap(line)+len(rn)-cap(rn):]) } else { //goland:noinspection GoUnhandledErrorResult sink.Write(line) } } return nil } func getLine(b []byte) []byte { if n := bytes.IndexByte(b, '\n'); n >= 0 { return b[:n+1] } return b } // Returns build statement's rule name func getBuildRuleName(line []byte) []byte { n := bytes.IndexByte(line, ':') if n <= 0 { return nil } ruleName := line[n+1:] if ruleName[0] == ' ' { ruleName = bytes.TrimLeft(ruleName, " ") } if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 { ruleName = ruleName[0:n] } return ruleName } // Returns rule statement's rule name func ruleName(lineAfterRule []byte) []byte { ruleName := lineAfterRule[len(rulePrefix):] if len(ruleName) == 0 { return ruleName } if ruleName[0] == ' ' { ruleName = bytes.TrimLeft(ruleName, " ") } if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 { ruleName = ruleName[0:n] } return ruleName } ================================================ FILE: tools/canoninja/canoninja_test.go ================================================ package canoninja import ( "bytes" "testing" ) func TestGenerate(t *testing.T) { tests := []struct { name string in []byte wantSink string wantErr bool }{ { name: "1", in: []byte(` rule rule1 abcd rule rule2 abcd build x: rule1 `), wantSink: ` rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b abcd rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b abcd build x: R9c97aba7f61994be6862f5ea9a62d26130c7f48b `, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sink := &bytes.Buffer{} err := Generate("", tt.in, sink) if (err != nil) != tt.wantErr { t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr) return } if gotSink := sink.String(); gotSink != tt.wantSink { t.Errorf("Generate() gotSink = %v, want %v", gotSink, tt.wantSink) } }) } } ================================================ FILE: tools/canoninja/cmd/canoninja.go ================================================ package main /* Canoninja reads a Ninja file and changes the rule names to be the digest of the rule contents. Feed it to a filter that extracts only build statements, sort them, and you will have a crude but effective tool to find small differences between two Ninja files. */ import ( "canoninja" "flag" "fmt" "os" ) func main() { flag.Parse() files := flag.Args() if len(files) == 0 { files = []string{"/dev/stdin"} } rc := 0 for _, f := range files { if buffer, err := os.ReadFile(f); err == nil { err = canoninja.Generate(f, buffer, os.Stdout) if err != nil { fmt.Fprintln(os.Stderr, err) rc = 1 } } else { fmt.Fprintf(os.Stderr, "%s: %s\n", f, err) rc = 1 } } os.Exit(rc) } ================================================ FILE: tools/canoninja/go.mod ================================================ module canoninja go 1.19 ================================================ FILE: tools/characteristics_rro_generator.py ================================================ #!/usr/bin/env python3 import sys if __name__ == '__main__': if len(sys.argv) != 3: sys.exit(f"usage: {sys_argv[0]} target_package_name output\n") with open(sys.argv[2], "w") as f: f.write(f''' ''') ================================================ FILE: tools/check-flagged-apis/Android.bp ================================================ // Copyright (C) 2024 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_team: "trendy_team_updatable_sdk_apis", default_applicable_licenses: ["Android-Apache-2.0"], } java_defaults { name: "check-flagged-apis-defaults", srcs: [ "src/com/android/checkflaggedapis/Main.kt", ], static_libs: [ "libaconfig_java_proto_lite", "metalava-signature-reader", "metalava-tools-common-m2-deps", ], } java_binary_host { name: "check-flagged-apis", defaults: [ "check-flagged-apis-defaults", ], main_class: "com.android.checkflaggedapis.Main", } java_test_host { name: "check-flagged-apis-test", defaults: [ "check-flagged-apis-defaults", ], srcs: [ "src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt", ], static_libs: [ "junit", ], } ================================================ FILE: tools/check-flagged-apis/OWNERS ================================================ amhk@google.com gurpreetgs@google.com michaelwr@google.com paulduffin@google.com ================================================ FILE: tools/check-flagged-apis/check-flagged-apis.sh ================================================ #!/bin/bash # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Run check-flagged-apis for public APIs and the three @SystemApi flavours. # # This script expects an argument to tell it which subcommand of # check-flagged-apis to execute. Run the script without any arguments to see # the valid options. # # Remember to lunch to select the relevant release config before running this script. source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../shell_utils.sh require_top PUBLIC_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_public_generated-api-versions.xml SYSTEM_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_system_generated-api-versions.xml SYSTEM_SERVER_XML_VERSONS=out/target/common/obj/PACKAGING/api_versions_system_server_complete_generated-api-versions.xml MODULE_LIB_XML_VERSIONS=out/target/common/obj/PACKAGING/api_versions_module_lib_complete_generated-api-versions.xml function m() { $(gettop)/build/soong/soong_ui.bash --build-mode --all-modules --dir="$(pwd)" "$@" } function build() { m \ check-flagged-apis \ all_aconfig_declarations \ frameworks-base-api-current.txt \ frameworks-base-api-system-current.txt \ frameworks-base-api-system-server-current.txt \ frameworks-base-api-module-lib-current.txt \ $PUBLIC_XML_VERSIONS \ $SYSTEM_XML_VERSIONS \ $SYSTEM_SERVER_XML_VERSONS \ $MODULE_LIB_XML_VERSIONS } function noop() { true } function aninja() { local T="$(gettop)" (\cd "${T}" && prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-${TARGET_PRODUCT}.ninja "$@") } function path_to_api_signature_file { aninja -t query device_"$1"_all_targets | grep -A1 -e input: | tail -n1 } function run_check() { local errors=0 echo "# current" check-flagged-apis check \ --api-signature $(path_to_api_signature_file "frameworks-base-api-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \ --api-versions $PUBLIC_XML_VERSIONS (( errors += $? )) echo echo "# system-current" check-flagged-apis check \ --api-signature $(path_to_api_signature_file "frameworks-base-api-system-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \ --api-versions $SYSTEM_XML_VERSIONS (( errors += $? )) echo echo "# system-server-current" check-flagged-apis check \ --api-signature $(path_to_api_signature_file "frameworks-base-api-system-server-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \ --api-versions $SYSTEM_SERVER_XML_VERSONS (( errors += $? )) echo echo "# module-lib" check-flagged-apis check \ --api-signature $(path_to_api_signature_file "frameworks-base-api-module-lib-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb \ --api-versions $MODULE_LIB_XML_VERSIONS (( errors += $? )) return $errors } function run_list() { echo "# current" check-flagged-apis list \ --api-signature $(path_to_api_signature_file "frameworks-base-api-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb echo echo "# system-current" check-flagged-apis list \ --api-signature $(path_to_api_signature_file "frameworks-base-api-system-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb echo echo "# system-server-current" check-flagged-apis list \ --api-signature $(path_to_api_signature_file "frameworks-base-api-system-server-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb echo echo "# module-lib" check-flagged-apis list \ --api-signature $(path_to_api_signature_file "frameworks-base-api-module-lib-current.txt") \ --flag-values $(gettop)/out/soong/.intermediates/all_aconfig_declarations.pb } build_cmd=build if [[ "$1" == "--skip-build" ]]; then build_cmd=noop shift 1 fi case "$1" in check) $build_cmd && run_check ;; list) $build_cmd && run_list ;; *) echo "usage: $(basename $0): [--skip-build] check|list"; exit 1 esac ================================================ FILE: tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.checkflaggedapis import android.aconfig.Aconfig import android.aconfig.Aconfig.flag_state.DISABLED import android.aconfig.Aconfig.flag_state.ENABLED import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 private val API_SIGNATURE = """ // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public final class Clazz { ctor @FlaggedApi("android.flag.foo") public Clazz(); field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1 method @FlaggedApi("android.flag.foo") public int getErrorCode(); method @FlaggedApi("android.flag.foo") public boolean setData(int, int[][], @NonNull android.util.Utility); method @FlaggedApi("android.flag.foo") public boolean setVariableData(int, android.util.Atom...); method @FlaggedApi("android.flag.foo") public boolean innerClassArg(android.Clazz.Builder); } @FlaggedApi("android.flag.bar") public static class Clazz.Builder { } } """ .trim() private val API_VERSIONS = """ """ .trim() private fun generateFlagsProto( fooState: Aconfig.flag_state, barState: Aconfig.flag_state ): InputStream { val fooFlag = Aconfig.parsed_flag .newBuilder() .setPackage("android.flag") .setName("foo") .setState(fooState) .setPermission(Aconfig.flag_permission.READ_ONLY) .build() val barFlag = Aconfig.parsed_flag .newBuilder() .setPackage("android.flag") .setName("bar") .setState(barState) .setPermission(Aconfig.flag_permission.READ_ONLY) .build() val flags = Aconfig.parsed_flags.newBuilder().addParsedFlag(fooFlag).addParsedFlag(barFlag).build() val binaryProto = ByteArrayOutputStream() flags.writeTo(binaryProto) return ByteArrayInputStream(binaryProto.toByteArray()) } @RunWith(JUnit4::class) class CheckFlaggedApisTest { @Test fun testParseApiSignature() { val expected = setOf( Pair( Symbol.createClass("android/Clazz", "java/lang/Object", setOf()), Flag("android.flag.foo")), Pair(Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")), Pair(Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")), Pair(Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")), Pair( Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"), Flag("android.flag.foo")), Pair( Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"), Flag("android.flag.foo")), Pair( Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Flag("android.flag.foo")), Pair( Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()), Flag("android.flag.bar")), ) val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()) assertEquals(expected, actual) } @Test fun testParseApiSignatureInterfacesInheritFromJavaLangObject() { val apiSignature = """ // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public interface Interface { } } """ .trim() val expected = setOf( Pair( Symbol.createClass("android/Interface", "java/lang/Object", setOf()), Flag("android.flag.foo"))) val actual = parseApiSignature("in-memory", apiSignature.byteInputStream()) assertEquals(expected, actual) } @Test fun testParseFlagValues() { val expected: Map = mapOf(Flag("android.flag.foo") to true, Flag("android.flag.bar") to true) val actual = parseFlagValues(generateFlagsProto(ENABLED, ENABLED)) assertEquals(expected, actual) } @Test fun testParseApiVersions() { val expected: Set = setOf( Symbol.createClass("android/Clazz", "java/lang/Object", setOf()), Symbol.createMethod("android/Clazz", "Clazz()"), Symbol.createField("android/Clazz", "FOO"), Symbol.createMethod("android/Clazz", "getErrorCode()"), Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"), Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"), Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()), ) val actual = parseApiVersions(API_VERSIONS.byteInputStream()) assertEquals(expected, actual) } @Test fun testParseApiVersionsNestedClasses() { val apiVersions = """ """ .trim() val expected: Set = setOf( Symbol.createClass("android/Clazz/Foo/Bar", "java/lang/Object", setOf()), Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"), ) val actual = parseApiVersions(apiVersions.byteInputStream()) assertEquals(expected, actual) } @Test fun testFindErrorsNoErrors() { val expected = setOf() val actual = findErrors( parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()), parseFlagValues(generateFlagsProto(ENABLED, ENABLED)), parseApiVersions(API_VERSIONS.byteInputStream())) assertEquals(expected, actual) } @Test fun testFindErrorsVerifyImplements() { val apiSignature = """ // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public final class Clazz implements android.Interface { method @FlaggedApi("android.flag.foo") public boolean foo(); method @FlaggedApi("android.flag.foo") public boolean bar(); } public interface Interface { method public boolean bar(); } } """ .trim() val apiVersions = """ """ .trim() val expected = setOf() val actual = findErrors( parseApiSignature("in-memory", apiSignature.byteInputStream()), parseFlagValues(generateFlagsProto(ENABLED, ENABLED)), parseApiVersions(apiVersions.byteInputStream())) assertEquals(expected, actual) } @Test fun testFindErrorsVerifySuperclass() { val apiSignature = """ // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public final class C extends android.B { method @FlaggedApi("android.flag.foo") public boolean c(); method @FlaggedApi("android.flag.foo") public boolean b(); method @FlaggedApi("android.flag.foo") public boolean a(); } public final class B extends android.A { method public boolean b(); } public final class A { method public boolean a(); } } """ .trim() val apiVersions = """ """ .trim() val expected = setOf() val actual = findErrors( parseApiSignature("in-memory", apiSignature.byteInputStream()), parseFlagValues(generateFlagsProto(ENABLED, ENABLED)), parseApiVersions(apiVersions.byteInputStream())) assertEquals(expected, actual) } @Test fun testNestedFlagsOuterFlagWins() { val apiSignature = """ // Signature format: 2.0 package android { @FlaggedApi("android.flag.foo") public final class A { method @FlaggedApi("android.flag.bar") public boolean method(); } @FlaggedApi("android.flag.bar") public final class B { method @FlaggedApi("android.flag.foo") public boolean method(); } } """ .trim() val apiVersions = """ """ .trim() val expected = setOf() val actual = findErrors( parseApiSignature("in-memory", apiSignature.byteInputStream()), parseFlagValues(generateFlagsProto(DISABLED, ENABLED)), parseApiVersions(apiVersions.byteInputStream())) assertEquals(expected, actual) } @Test fun testFindErrorsDisabledFlaggedApiIsPresent() { val expected = setOf( DisabledFlaggedApiIsPresentError( Symbol.createClass("android/Clazz", "java/lang/Object", setOf()), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Flag("android.flag.foo")), DisabledFlaggedApiIsPresentError( Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()), Flag("android.flag.bar")), ) val actual = findErrors( parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()), parseFlagValues(generateFlagsProto(DISABLED, DISABLED)), parseApiVersions(API_VERSIONS.byteInputStream())) assertEquals(expected, actual) } @Test fun testListFlaggedApis() { val expected = listOf( "android.flag.bar DISABLED android/Clazz/Builder", "android.flag.foo ENABLED android/Clazz", "android.flag.foo ENABLED android/Clazz/Clazz()", "android.flag.foo ENABLED android/Clazz/FOO", "android.flag.foo ENABLED android/Clazz/getErrorCode()", "android.flag.foo ENABLED android/Clazz/innerClassArg(Landroid/Clazz/Builder;)", "android.flag.foo ENABLED android/Clazz/setData(I[[ILandroid/util/Utility;)", "android.flag.foo ENABLED android/Clazz/setVariableData(I[Landroid/util/Atom;)") val actual = listFlaggedApis( parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()), parseFlagValues(generateFlagsProto(ENABLED, DISABLED))) assertEquals(expected, actual) } } ================================================ FILE: tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt ================================================ /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:JvmName("Main") package com.android.checkflaggedapis import android.aconfig.Aconfig import com.android.tools.metalava.model.BaseItemVisitor import com.android.tools.metalava.model.CallableItem import com.android.tools.metalava.model.ClassItem import com.android.tools.metalava.model.FieldItem import com.android.tools.metalava.model.Item import com.android.tools.metalava.model.text.ApiFile import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.ProgramResult import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.options.help import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.required import com.github.ajalt.clikt.parameters.types.path import java.io.InputStream import javax.xml.parsers.DocumentBuilderFactory import org.w3c.dom.Node /** * Class representing the fully qualified name of a class, method or field. * * This tool reads a multitude of input formats all of which represents the fully qualified path to * a Java symbol slightly differently. To keep things consistent, all parsed APIs are converted to * Symbols. * * Symbols are encoded using the format similar to the one described in section 4.3.2 of the JVM * spec [1], that is, "package.class.inner-class.method(int, int[], android.util.Clazz)" is * represented as *

 *   package.class.inner-class.method(II[Landroid/util/Clazz;)
 * 
 *
 * Where possible, the format has been simplified (to make translation of the
 * various input formats easier): for instance, only / is used as delimiter (#
 * and $ are never used).
 *
 * 1. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2
 */
internal sealed class Symbol {
  companion object {
    private val FORBIDDEN_CHARS = listOf('#', '$', '.')

    fun createClass(clazz: String, superclass: String?, interfaces: Set): Symbol {
      return ClassSymbol(
          toInternalFormat(clazz),
          superclass?.let { toInternalFormat(it) },
          interfaces.map { toInternalFormat(it) }.toSet())
    }

    fun createField(clazz: String, field: String): Symbol {
      require(!field.contains("(") && !field.contains(")"))
      return MemberSymbol(toInternalFormat(clazz), toInternalFormat(field))
    }

    fun createMethod(clazz: String, method: String): Symbol {
      return MemberSymbol(toInternalFormat(clazz), toInternalFormat(method))
    }

    protected fun toInternalFormat(name: String): String {
      var internalName = name
      for (ch in FORBIDDEN_CHARS) {
        internalName = internalName.replace(ch, '/')
      }
      return internalName
    }
  }

  abstract fun toPrettyString(): String
}

internal data class ClassSymbol(
    val clazz: String,
    val superclass: String?,
    val interfaces: Set
) : Symbol() {
  override fun toPrettyString(): String = "$clazz"
}

internal data class MemberSymbol(val clazz: String, val member: String) : Symbol() {
  override fun toPrettyString(): String = "$clazz/$member"
}

/**
 * Class representing the fully qualified name of an aconfig flag.
 *
 * This includes both the flag's package and name, separated by a dot, e.g.:
 * 
 *   com.android.aconfig.test.disabled_ro
 * 
 */
@JvmInline
internal value class Flag(val name: String) {
  override fun toString(): String = name.toString()
}

internal sealed class ApiError {
  abstract val symbol: Symbol
  abstract val flag: Flag
}

internal data class EnabledFlaggedApiNotPresentError(
    override val symbol: Symbol,
    override val flag: Flag
) : ApiError() {
  override fun toString(): String {
    return "error: enabled @FlaggedApi not present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag"
  }
}

internal data class DisabledFlaggedApiIsPresentError(
    override val symbol: Symbol,
    override val flag: Flag
) : ApiError() {
  override fun toString(): String {
    return "error: disabled @FlaggedApi is present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag"
  }
}

internal data class UnknownFlagError(override val symbol: Symbol, override val flag: Flag) :
    ApiError() {
  override fun toString(): String {
    return "error: unknown flag: symbol=${symbol.toPrettyString()} flag=$flag"
  }
}

val ARG_API_SIGNATURE = "--api-signature"
val ARG_API_SIGNATURE_HELP =
    """
Path to API signature file.
Usually named *current.txt.
Tip: `m frameworks-base-api-current.txt` will generate a file that includes all platform and mainline APIs.
"""

val ARG_FLAG_VALUES = "--flag-values"
val ARG_FLAG_VALUES_HELP =
    """
Path to aconfig parsed_flags binary proto file.
Tip: `m all_aconfig_declarations` will generate a file that includes all information about all flags.
"""

val ARG_API_VERSIONS = "--api-versions"
val ARG_API_VERSIONS_HELP =
    """
Path to API versions XML file.
Usually named xml-versions.xml.
Tip: `m sdk dist` will generate a file that includes all platform and mainline APIs.
"""

class MainCommand : CliktCommand() {
  override fun run() {}
}

class CheckCommand :
    CliktCommand(
        help =
            """
Check that all flagged APIs are used in the correct way.

This tool reads the API signature file and checks that all flagged APIs are used in the correct way.

The tool will exit with a non-zero exit code if any flagged APIs are found to be used in the incorrect way.
""") {
  private val apiSignaturePath by
      option(ARG_API_SIGNATURE)
          .help(ARG_API_SIGNATURE_HELP)
          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
          .required()
  private val flagValuesPath by
      option(ARG_FLAG_VALUES)
          .help(ARG_FLAG_VALUES_HELP)
          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
          .required()
  private val apiVersionsPath by
      option(ARG_API_VERSIONS)
          .help(ARG_API_VERSIONS_HELP)
          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
          .required()

  override fun run() {
    val flaggedSymbols =
        apiSignaturePath.toFile().inputStream().use {
          parseApiSignature(apiSignaturePath.toString(), it)
        }
    val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }
    val exportedSymbols = apiVersionsPath.toFile().inputStream().use { parseApiVersions(it) }
    val errors = findErrors(flaggedSymbols, flags, exportedSymbols)
    for (e in errors) {
      println(e)
    }
    throw ProgramResult(errors.size)
  }
}

class ListCommand :
    CliktCommand(
        help =
            """
List all flagged APIs and corresponding flags.

The output format is "  ", one line per API.

The output can be post-processed by e.g. piping it to grep to filter out only enabled APIs, or all APIs guarded by a given flag.
""") {
  private val apiSignaturePath by
      option(ARG_API_SIGNATURE)
          .help(ARG_API_SIGNATURE_HELP)
          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
          .required()
  private val flagValuesPath by
      option(ARG_FLAG_VALUES)
          .help(ARG_FLAG_VALUES_HELP)
          .path(mustExist = true, canBeDir = false, mustBeReadable = true)
          .required()

  override fun run() {
    val flaggedSymbols =
        apiSignaturePath.toFile().inputStream().use {
          parseApiSignature(apiSignaturePath.toString(), it)
        }
    val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }
    val output = listFlaggedApis(flaggedSymbols, flags)
    if (output.isNotEmpty()) {
      println(output.joinToString("\n"))
    }
  }
}

internal fun parseApiSignature(path: String, input: InputStream): Set> {
  val output = mutableSetOf>()
  val visitor =
      object : BaseItemVisitor() {
        override fun visitClass(cls: ClassItem) {
          getFlagOrNull(cls)?.let { flag ->
            val symbol =
                Symbol.createClass(
                    cls.baselineElementId(),
                    if (cls.isInterface()) {
                      "java/lang/Object"
                    } else {
                      cls.superClass()?.baselineElementId()
                    },
                    cls.allInterfaces()
                        .map { it.baselineElementId() }
                        .filter { it != cls.baselineElementId() }
                        .toSet())
            output.add(Pair(symbol, flag))
          }
        }

        override fun visitField(field: FieldItem) {
          getFlagOrNull(field)?.let { flag ->
            val symbol =
                Symbol.createField(field.containingClass().baselineElementId(), field.name())
            output.add(Pair(symbol, flag))
          }
        }

        override fun visitCallable(callable: CallableItem) {
          getFlagOrNull(callable)?.let { flag ->
            val callableSignature = buildString {
              append(callable.name())
              append("(")
              callable.parameters().joinTo(this, separator = "") { it.type().internalName() }
              append(")")
            }
            val symbol =
                Symbol.createMethod(callable.containingClass().qualifiedName(), callableSignature)
            output.add(Pair(symbol, flag))
          }
        }

        private fun getFlagOrNull(item: Item): Flag? {
          return item.modifiers
              .findAnnotation("android.annotation.FlaggedApi")
              ?.findAttribute("value")
              ?.legacyValue
              ?.let { Flag(it.value() as String) }
        }
      }
  val codebase = ApiFile.parseApi(path, input)
  codebase.accept(visitor)
  return output
}

internal fun parseFlagValues(input: InputStream): Map {
  val parsedFlags = Aconfig.parsed_flags.parseFrom(input).getParsedFlagList()
  return parsedFlags.associateBy(
      { Flag("${it.getPackage()}.${it.getName()}") },
      { it.getState() == Aconfig.flag_state.ENABLED })
}

internal fun parseApiVersions(input: InputStream): Set {
  fun Node.getAttribute(name: String): String? = getAttributes()?.getNamedItem(name)?.getNodeValue()

  val output = mutableSetOf()
  val factory = DocumentBuilderFactory.newInstance()
  val parser = factory.newDocumentBuilder()
  val document = parser.parse(input)

  val classes = document.getElementsByTagName("class")
  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
  for (i in 0.rangeUntil(classes.getLength())) {
    val cls = classes.item(i)
    val className =
        requireNotNull(cls.getAttribute("name")) {
          "Bad XML:  element without name attribute"
        }
    var superclass: String? = null
    val interfaces = mutableSetOf()
    val children = cls.getChildNodes()
    for (j in 0.rangeUntil(children.getLength())) {
      val child = children.item(j)
      when (child.getNodeName()) {
        "extends" -> {
          superclass =
              requireNotNull(child.getAttribute("name")) {
                "Bad XML:  element without name attribute"
              }
        }
        "implements" -> {
          val interfaceName =
              requireNotNull(child.getAttribute("name")) {
                "Bad XML:  element without name attribute"
              }
          interfaces.add(interfaceName)
        }
      }
    }
    output.add(Symbol.createClass(className, superclass, interfaces))
  }

  val fields = document.getElementsByTagName("field")
  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
  for (i in 0.rangeUntil(fields.getLength())) {
    val field = fields.item(i)
    val fieldName =
        requireNotNull(field.getAttribute("name")) {
          "Bad XML:  element without name attribute"
        }
    val className =
        requireNotNull(field.getParentNode()?.getAttribute("name")) {
          "Bad XML: top level  element"
        }
    output.add(Symbol.createField(className, fieldName))
  }

  val methods = document.getElementsByTagName("method")
  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
  for (i in 0.rangeUntil(methods.getLength())) {
    val method = methods.item(i)
    val methodSignature =
        requireNotNull(method.getAttribute("name")) {
          "Bad XML:  element without name attribute"
        }
    val methodSignatureParts = methodSignature.split(Regex("\\(|\\)"))
    if (methodSignatureParts.size != 3) {
      throw Exception("Bad XML: method signature '$methodSignature'")
    }
    var (methodName, methodArgs, _) = methodSignatureParts
    val packageAndClassName =
        requireNotNull(method.getParentNode()?.getAttribute("name")) {
              "Bad XML: top level  element, or  element missing name attribute"
            }
            .replace("$", "/")
    if (methodName == "") {
      methodName = packageAndClassName.split("/").last()
    }
    output.add(Symbol.createMethod(packageAndClassName, "$methodName($methodArgs)"))
  }

  return output
}

/**
 * Find errors in the given data.
 *
 * @param flaggedSymbolsInSource the set of symbols that are flagged in the source code
 * @param flags the set of flags and their values
 * @param symbolsInOutput the set of symbols that are present in the output
 * @return the set of errors found
 */
internal fun findErrors(
    flaggedSymbolsInSource: Set>,
    flags: Map,
    symbolsInOutput: Set
): Set {
  fun Set.containsSymbol(symbol: Symbol): Boolean {
    // trivial case: the symbol is explicitly listed in api-versions.xml
    if (contains(symbol)) {
      return true
    }

    // non-trivial case: the symbol could be part of the surrounding class'
    // super class or interfaces
    val (className, memberName) =
        when (symbol) {
          is ClassSymbol -> return false
          is MemberSymbol -> {
            Pair(symbol.clazz, symbol.member)
          }
        }
    val clazz = find { it is ClassSymbol && it.clazz == className } as? ClassSymbol?
    if (clazz == null) {
      return false
    }

    for (interfaceName in clazz.interfaces) {
      // createMethod is the same as createField, except it allows parenthesis
      val interfaceSymbol = Symbol.createMethod(interfaceName, memberName)
      if (contains(interfaceSymbol)) {
        return true
      }
    }

    if (clazz.superclass != null) {
      val superclassSymbol = Symbol.createMethod(clazz.superclass, memberName)
      return containsSymbol(superclassSymbol)
    }

    return false
  }

  /**
   * Returns whether the given flag is enabled for the given symbol.
   *
   * A flagged member inside a flagged class is ignored (and the flag value considered disabled) if
   * the class' flag is disabled.
   *
   * @param symbol the symbol to check
   * @param flag the flag to check
   * @return whether the flag is enabled for the given symbol
   */
  fun isFlagEnabledForSymbol(symbol: Symbol, flag: Flag): Boolean {
    when (symbol) {
      is ClassSymbol -> return flags.getValue(flag)
      is MemberSymbol -> {
        val memberFlagValue = flags.getValue(flag)
        if (!memberFlagValue) {
          return false
        }
        // Special case: if the MemberSymbol's flag is enabled, but the outer
        // ClassSymbol's flag (if the class is flagged) is disabled, consider
        // the MemberSymbol's flag as disabled:
        //
        //   @FlaggedApi(this-flag-is-disabled) Clazz {
        //       @FlaggedApi(this-flag-is-enabled) method(); // The Clazz' flag "wins"
        //   }
        //
        // Note: the current implementation does not handle nested classes.
        val classFlagValue =
            flaggedSymbolsInSource
                .find { it.first.toPrettyString() == symbol.clazz }
                ?.let { flags.getValue(it.second) } ?: true
        return classFlagValue
      }
    }
  }

  val errors = mutableSetOf()
  for ((symbol, flag) in flaggedSymbolsInSource) {
    try {
      if (isFlagEnabledForSymbol(symbol, flag)) {
        if (!symbolsInOutput.containsSymbol(symbol)) {
          errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))
        }
      } else {
        if (symbolsInOutput.containsSymbol(symbol)) {
          errors.add(DisabledFlaggedApiIsPresentError(symbol, flag))
        }
      }
    } catch (e: NoSuchElementException) {
      errors.add(UnknownFlagError(symbol, flag))
    }
  }
  return errors
}

/**
 * Collect all known info about all @FlaggedApi annotated APIs.
 *
 * Each API will be represented as a String, on the format
 * 
 *   <fully-qualified-name-of-flag< <state-of-flag< <API<
 * 
* * @param flaggedSymbolsInSource the set of symbols that are flagged in the source code * @param flags the set of flags and their values * @return a list of Strings encoding API data using the format described above, sorted * alphabetically */ internal fun listFlaggedApis( flaggedSymbolsInSource: Set>, flags: Map ): List { val output = mutableListOf() for ((symbol, flag) in flaggedSymbolsInSource) { val flagState = when (flags.get(flag)) { true -> "ENABLED" false -> "DISABLED" null -> "UNKNOWN" } output.add("$flag $flagState ${symbol.toPrettyString()}") } output.sort() return output } fun main(args: Array) = MainCommand().subcommands(CheckCommand(), ListCommand()).main(args) ================================================ FILE: tools/check_elf_file.py ================================================ #!/usr/bin/env python3 # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ELF file checker. This command ensures all undefined symbols in an ELF file can be resolved to global (or weak) symbols defined in shared objects specified in DT_NEEDED entries. """ from __future__ import print_function import argparse import collections import os import os.path import re import struct import subprocess import sys _ELF_MAGIC = b'\x7fELF' # Known machines _EM_386 = 3 _EM_ARM = 40 _EM_X86_64 = 62 _EM_AARCH64 = 183 _32_BIT_MACHINES = {_EM_386, _EM_ARM} _64_BIT_MACHINES = {_EM_X86_64, _EM_AARCH64} _KNOWN_MACHINES = _32_BIT_MACHINES | _64_BIT_MACHINES # ELF header struct _ELF_HEADER_STRUCT = ( ('ei_magic', '4s'), ('ei_class', 'B'), ('ei_data', 'B'), ('ei_version', 'B'), ('ei_osabi', 'B'), ('ei_pad', '8s'), ('e_type', 'H'), ('e_machine', 'H'), ('e_version', 'I'), ) _ELF_HEADER_STRUCT_FMT = ''.join(_fmt for _, _fmt in _ELF_HEADER_STRUCT) ELFHeader = collections.namedtuple( 'ELFHeader', [_name for _name, _ in _ELF_HEADER_STRUCT]) ELF = collections.namedtuple( 'ELF', ('alignments', 'dt_soname', 'dt_needed', 'imported', 'exported', 'header')) def _get_os_name(): """Get the host OS name.""" if sys.platform.startswith('linux'): return 'linux' if sys.platform.startswith('darwin'): return 'darwin' raise ValueError(sys.platform + ' is not supported') def _get_build_top(): """Find the build top of the source tree ($ANDROID_BUILD_TOP).""" prev_path = None curr_path = os.path.abspath(os.getcwd()) while prev_path != curr_path: if os.path.exists(os.path.join(curr_path, '.repo')): return curr_path prev_path = curr_path curr_path = os.path.dirname(curr_path) return None def _select_latest_llvm_version(versions): """Select the latest LLVM prebuilts version from a set of versions.""" pattern = re.compile('clang-r([0-9]+)([a-z]?)') found_rev = 0 found_ver = None for curr_ver in versions: match = pattern.match(curr_ver) if not match: continue curr_rev = int(match.group(1)) if not found_ver or curr_rev > found_rev or ( curr_rev == found_rev and curr_ver > found_ver): found_rev = curr_rev found_ver = curr_ver return found_ver def _get_latest_llvm_version(llvm_dir): """Find the latest LLVM prebuilts version from `llvm_dir`.""" return _select_latest_llvm_version(os.listdir(llvm_dir)) def _get_llvm_dir(): """Find the path to LLVM prebuilts.""" build_top = _get_build_top() llvm_prebuilts_base = os.environ.get('LLVM_PREBUILTS_BASE') if not llvm_prebuilts_base: llvm_prebuilts_base = os.path.join('prebuilts', 'clang', 'host') llvm_dir = os.path.join( build_top, llvm_prebuilts_base, _get_os_name() + '-x86') if not os.path.exists(llvm_dir): return None llvm_prebuilts_version = os.environ.get('LLVM_PREBUILTS_VERSION') if not llvm_prebuilts_version: llvm_prebuilts_version = _get_latest_llvm_version(llvm_dir) llvm_dir = os.path.join(llvm_dir, llvm_prebuilts_version) if not os.path.exists(llvm_dir): return None return llvm_dir def _get_llvm_readobj(): """Find the path to llvm-readobj executable.""" llvm_dir = _get_llvm_dir() llvm_readobj = os.path.join(llvm_dir, 'bin', 'llvm-readobj') return llvm_readobj if os.path.exists(llvm_readobj) else 'llvm-readobj' class ELFError(ValueError): """Generic ELF parse error""" pass class ELFInvalidMagicError(ELFError): """Invalid ELF magic word error""" def __init__(self): super(ELFInvalidMagicError, self).__init__('bad ELF magic') class ELFParser(object): """ELF file parser""" @classmethod def _read_elf_header(cls, elf_file_path): """Read the ELF magic word from the beginning of the file.""" with open(elf_file_path, 'rb') as elf_file: buf = elf_file.read(struct.calcsize(_ELF_HEADER_STRUCT_FMT)) try: return ELFHeader(*struct.unpack(_ELF_HEADER_STRUCT_FMT, buf)) except struct.error: return None @classmethod def open(cls, elf_file_path, llvm_readobj): """Open and parse the ELF file.""" # Parse the ELF header to check the magic word. header = cls._read_elf_header(elf_file_path) if not header or header.ei_magic != _ELF_MAGIC: raise ELFInvalidMagicError() # Run llvm-readobj and parse the output. return cls._read_llvm_readobj(elf_file_path, header, llvm_readobj) @classmethod def _find_prefix(cls, pattern, lines_it): """Iterate `lines_it` until finding a string that starts with `pattern`.""" for line in lines_it: if line.startswith(pattern): return True return False @classmethod def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj): """Run llvm-readobj and parse the output.""" cmd = [llvm_readobj, '--program-headers', '--dynamic-table', '--dyn-symbols', elf_file_path] out = subprocess.check_output(cmd, text=True) lines = out.splitlines() return cls._parse_llvm_readobj(elf_file_path, header, lines) @classmethod def _parse_llvm_readobj(cls, elf_file_path, header, lines): """Parse the output of llvm-readobj.""" lines_it = iter(lines) alignments = cls._parse_program_headers(lines_it) dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it) imported, exported = cls._parse_dynamic_symbols(lines_it) return ELF(alignments, dt_soname, dt_needed, imported, exported, header) _PROGRAM_HEADERS_START_PATTERN = 'ProgramHeaders [' _PROGRAM_HEADERS_END_PATTERN = ']' _PROGRAM_HEADER_START_PATTERN = 'ProgramHeader {' _PROGRAM_HEADER_TYPE_PATTERN = re.compile('^\\s+Type:\\s+(.*)$') _PROGRAM_HEADER_ALIGN_PATTERN = re.compile('^\\s+Alignment:\\s+(.*)$') _PROGRAM_HEADER_END_PATTERN = '}' @classmethod def _parse_program_headers(cls, lines_it): """Parse the dynamic table section.""" alignments = [] if not cls._find_prefix(cls._PROGRAM_HEADERS_START_PATTERN, lines_it): raise ELFError() for line in lines_it: # Parse each program header if line.strip() == cls._PROGRAM_HEADER_START_PATTERN: p_align = None p_type = None for line in lines_it: if line.strip() == cls._PROGRAM_HEADER_END_PATTERN: if not p_align: raise ELFError("Could not parse alignment from program header!") if not p_type: raise ELFError("Could not parse type from program header!") if p_type.startswith("PT_LOAD "): alignments.append(int(p_align)) break match = cls._PROGRAM_HEADER_TYPE_PATTERN.match(line) if match: p_type = match.group(1) match = cls._PROGRAM_HEADER_ALIGN_PATTERN.match(line) if match: p_align = match.group(1) if line == cls._PROGRAM_HEADERS_END_PATTERN: break return alignments _DYNAMIC_SECTION_START_PATTERN = 'DynamicSection [' _DYNAMIC_SECTION_NEEDED_PATTERN = re.compile( '^ 0x[0-9a-fA-F]+\\s+NEEDED\\s+Shared library: \\[(.*)\\]$') _DYNAMIC_SECTION_SONAME_PATTERN = re.compile( '^ 0x[0-9a-fA-F]+\\s+SONAME\\s+Library soname: \\[(.*)\\]$') _DYNAMIC_SECTION_END_PATTERN = ']' @classmethod def _parse_dynamic_table(cls, elf_file_path, lines_it): """Parse the dynamic table section.""" dt_soname = os.path.basename(elf_file_path) dt_needed = [] dynamic = cls._find_prefix(cls._DYNAMIC_SECTION_START_PATTERN, lines_it) if not dynamic: return (dt_soname, dt_needed) for line in lines_it: if line == cls._DYNAMIC_SECTION_END_PATTERN: break match = cls._DYNAMIC_SECTION_NEEDED_PATTERN.match(line) if match: dt_needed.append(match.group(1)) continue match = cls._DYNAMIC_SECTION_SONAME_PATTERN.match(line) if match: dt_soname = match.group(1) continue return (dt_soname, dt_needed) _DYNAMIC_SYMBOLS_START_PATTERN = 'DynamicSymbols [' _DYNAMIC_SYMBOLS_END_PATTERN = ']' _SYMBOL_ENTRY_START_PATTERN = ' Symbol {' _SYMBOL_ENTRY_PATTERN = re.compile('^ ([A-Za-z0-9_]+): (.*)$') _SYMBOL_ENTRY_PAREN_PATTERN = re.compile( '\\s+\\((?:(?:\\d+)|(?:0x[0-9a-fA-F]+))\\)$') _SYMBOL_ENTRY_END_PATTERN = ' }' @staticmethod def _parse_symbol_name(name_with_version): """Split `name_with_version` into name and version. This function may split at last occurrence of `@@` or `@`.""" pos = name_with_version.rfind('@') if pos == -1: name = name_with_version version = '' else: if pos > 0 and name_with_version[pos - 1] == '@': name = name_with_version[0:pos - 1] else: name = name_with_version[0:pos] version = name_with_version[pos + 1:] return (name, version) @classmethod def _parse_dynamic_symbols(cls, lines_it): """Parse dynamic symbol table and collect imported and exported symbols.""" imported = collections.defaultdict(set) exported = collections.defaultdict(set) for symbol in cls._parse_dynamic_symbols_internal(lines_it): name, version = cls._parse_symbol_name(symbol['Name']) if name: if symbol['Section'] == 'Undefined': if symbol['Binding'] != 'Weak': imported[name].add(version) else: if symbol['Binding'] != 'Local': exported[name].add(version) # Freeze the returned imported/exported dict. return (dict(imported), dict(exported)) @classmethod def _parse_dynamic_symbols_internal(cls, lines_it): """Parse symbols entries and yield each symbols.""" if not cls._find_prefix(cls._DYNAMIC_SYMBOLS_START_PATTERN, lines_it): return for line in lines_it: if line == cls._DYNAMIC_SYMBOLS_END_PATTERN: return if line == cls._SYMBOL_ENTRY_START_PATTERN: symbol = {} continue if line == cls._SYMBOL_ENTRY_END_PATTERN: yield symbol symbol = None continue match = cls._SYMBOL_ENTRY_PATTERN.match(line) if match: key = match.group(1) value = cls._SYMBOL_ENTRY_PAREN_PATTERN.sub('', match.group(2)) symbol[key] = value continue class Checker(object): """ELF file checker that checks DT_SONAME, DT_NEEDED, and symbols.""" def __init__(self, llvm_readobj): self._file_path = '' self._file_under_test = None self._shared_libs = [] self._llvm_readobj = llvm_readobj if sys.stderr.isatty(): _ERROR_TAG = '\033[0;1;31merror:\033[m' # Red error _NOTE_TAG = '\033[0;1;30mnote:\033[m' # Black note else: _ERROR_TAG = 'error:' # Red error _NOTE_TAG = 'note:' # Black note def _error(self, *args): """Emit an error to stderr.""" print(self._file_path + ': ' + self._ERROR_TAG, *args, file=sys.stderr) def _note(self, *args): """Emit a note to stderr.""" print(self._file_path + ': ' + self._NOTE_TAG, *args, file=sys.stderr) def _load_elf_file(self, path, skip_bad_elf_magic): """Load an ELF file from the `path`.""" try: return ELFParser.open(path, self._llvm_readobj) except (IOError, OSError): self._error('Failed to open "{}".'.format(path)) sys.exit(2) except ELFInvalidMagicError: if skip_bad_elf_magic: sys.exit(0) else: self._error('File "{}" must have a valid ELF magic word.'.format(path)) sys.exit(2) except: self._error('An unknown error occurred while opening "{}".'.format(path)) raise def load_file_under_test(self, path, skip_bad_elf_magic, skip_unknown_elf_machine): """Load file-under-test (either an executable or a shared lib).""" self._file_path = path self._file_under_test = self._load_elf_file(path, skip_bad_elf_magic) if skip_unknown_elf_machine and \ self._file_under_test.header.e_machine not in _KNOWN_MACHINES: sys.exit(0) def load_shared_libs(self, shared_lib_paths): """Load shared libraries.""" for path in shared_lib_paths: self._shared_libs.append(self._load_elf_file(path, False)) def check_dt_soname(self, soname): """Check whether DT_SONAME matches installation file name.""" if self._file_under_test.dt_soname != soname: self._error('DT_SONAME "{}" must be equal to the file name "{}".' .format(self._file_under_test.dt_soname, soname)) sys.exit(2) def check_dt_needed(self, system_shared_lib_names): """Check whether all DT_NEEDED entries are specified in the build system.""" missing_shared_libs = False # Collect the DT_SONAMEs from shared libs specified in the build system. specified_sonames = {lib.dt_soname for lib in self._shared_libs} # Chech whether all DT_NEEDED entries are specified. for lib in self._file_under_test.dt_needed: if lib not in specified_sonames: self._error(f'DT_NEEDED "{lib}" is not specified in shared_libs.') missing_shared_libs = True if missing_shared_libs: dt_needed = sorted(set(self._file_under_test.dt_needed)) modules = [re.sub('\\.so$', '', lib) for lib in dt_needed] # Remove system shared libraries from the suggestion since they are added # by default. modules = [name for name in modules if name not in system_shared_lib_names] self._note() self._note('Fix suggestions:') self._note( ' Android.bp: shared_libs: [' + ', '.join('"' + module + '"' for module in modules) + '],') self._note( ' Android.mk: LOCAL_SHARED_LIBRARIES := ' + ' '.join(modules)) self._note() self._note('If the fix above doesn\'t work, bypass this check with:') self._note(' Android.bp: check_elf_files: false,') self._note(' Android.mk: LOCAL_CHECK_ELF_FILES := false') sys.exit(2) def check_max_page_size(self, max_page_size): if self._file_under_test.header.e_machine in _32_BIT_MACHINES: # Skip test on 32-bit machines. 16 KB pages is an arm64 feature # and no 32-bit systems in Android use it. return for alignment in self._file_under_test.alignments: if alignment % max_page_size != 0: self._error(f'Load segment has alignment {alignment} but ' f'{max_page_size} required.') self._note() self._note('Fix suggestions:') self._note(f' use linker flag "-Wl,-z,max-page-size={max_page_size}" ' f'when compiling this lib') self._note() self._note('If the fix above doesn\'t work, bypass this check with:') self._note(' Android.bp: ignore_max_page_size: true,') self._note(' Android.mk: LOCAL_IGNORE_MAX_PAGE_SIZE := true') self._note(' Device mk: PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE := false') # TODO: instead of exiting immediately, we may want to collect the # errors from all checks and emit them at once sys.exit(2) @staticmethod def _find_symbol(lib, name, version): """Check whether the symbol name and version matches a definition in lib.""" try: lib_sym_vers = lib.exported[name] except KeyError: return False if version == '': # Symbol version is not requested return True return version in lib_sym_vers @classmethod def _find_symbol_from_libs(cls, libs, name, version): """Check whether the symbol name and version is defined in one of the shared libraries in libs.""" for lib in libs: if cls._find_symbol(lib, name, version): return lib return None def check_symbols(self): """Check whether all undefined symbols are resolved to a definition.""" all_elf_files = [self._file_under_test] + self._shared_libs missing_symbols = [] for sym, imported_vers in self._file_under_test.imported.items(): for imported_ver in imported_vers: lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver) if not lib: missing_symbols.append((sym, imported_ver)) if missing_symbols: for sym, ver in sorted(missing_symbols): if ver: sym += '@' + ver self._error(f'Unresolved symbol: {sym}') self._note() self._note('Some dependencies might be changed, thus the symbol(s) ' 'above cannot be resolved.') self._note(f'Please re-build the prebuilt file: "{self._file_path}".') self._note() self._note('If this is a new prebuilt file and it is designed to have ' 'unresolved symbols, add one of the following properties:') self._note(' Android.bp: allow_undefined_symbols: true,') self._note(' Android.mk: LOCAL_ALLOW_UNDEFINED_SYMBOLS := true') sys.exit(2) def _parse_args(): """Parse command line options.""" parser = argparse.ArgumentParser() # Input file parser.add_argument('file', help='Path to the input file to be checked') parser.add_argument('--soname', help='Shared object name of the input file') # Shared library dependencies parser.add_argument('--shared-lib', action='append', default=[], help='Path to shared library dependencies') # System Shared library names parser.add_argument('--system-shared-lib', action='append', default=[], help='System shared libraries to be hidden from fix ' 'suggestions') # Check options parser.add_argument('--skip-bad-elf-magic', action='store_true', help='Ignore the input file without the ELF magic word') parser.add_argument('--skip-unknown-elf-machine', action='store_true', help='Ignore the input file with unknown machine ID') parser.add_argument('--allow-undefined-symbols', action='store_true', help='Ignore unresolved undefined symbols') parser.add_argument('--max-page-size', action='store', type=int, help='Required page size alignment support') # Other options parser.add_argument('--llvm-readobj', help='Path to the llvm-readobj executable') return parser.parse_args() def main(): """Main function""" args = _parse_args() llvm_readobj = args.llvm_readobj if not llvm_readobj: llvm_readobj = _get_llvm_readobj() # Load ELF files checker = Checker(llvm_readobj) checker.load_file_under_test( args.file, args.skip_bad_elf_magic, args.skip_unknown_elf_machine) checker.load_shared_libs(args.shared_lib) # Run checks if args.soname: checker.check_dt_soname(args.soname) checker.check_dt_needed(args.system_shared_lib) if args.max_page_size: checker.check_max_page_size(args.max_page_size) if not args.allow_undefined_symbols: checker.check_symbols() if __name__ == '__main__': main() ================================================ FILE: tools/check_identical_lib.sh ================================================ #!/bin/bash set -e STRIP_PATH="${1}" CORE="${2}" VENDOR="${3}" TMPDIR="$(mktemp -d ${CORE}.vndk_lib_check.XXXXXXXX)" stripped_core="${TMPDIR}/core" stripped_vendor="${TMPDIR}/vendor" function cleanup() { rm -f "${stripped_core}" "${stripped_vendor}" rmdir "${TMPDIR}" } trap cleanup EXIT function strip_lib() { ${STRIP_PATH} \ -i ${1} \ -o ${2} \ -d /dev/null \ --remove-build-id } strip_lib ${CORE} ${stripped_core} strip_lib ${VENDOR} ${stripped_vendor} if ! cmp -s ${stripped_core} ${stripped_vendor}; then echo "ERROR: VNDK library $(basename ${CORE%.so}) has different core and" \ "vendor variants! This means that the copy used in the system.img/etc" \ "and vendor.img/etc images are different. In order to preserve space on" \ "some devices, it is helpful if they are the same. Frequently, " \ "libraries are different because they or their dependencies compile" \ "things based on the macro '__ANDROID_VNDK__' or they specify custom" \ "options under 'target: { vendor: { ... } }'. Here are some possible" \ "resolutions:" echo "ERROR: 1). Remove differences, possibly using the libvndksupport" \ "function android_is_in_vendor_process in order to turn this into a" \ "runtime difference." echo "ERROR: 2). Add the library to the VndkMustUseVendorVariantList" \ "variable in build/soong/cc/config/vndk.go, which is used to" \ "acknowledge this difference." exit 1 fi ================================================ FILE: tools/check_radio_versions.py ================================================ #!/usr/bin/env python # # Copyright (C) 2012 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import os try: from hashlib import sha1 except ImportError: from sha import sha as sha1 import argparse parser = argparse.ArgumentParser() parser.add_argument("--board_info_txt", nargs="?", required=True) parser.add_argument("--board_info_check", nargs="*", required=True) args = parser.parse_args() if not args.board_info_txt: sys.exit(0) build_info = {} f = open(args.board_info_txt) for line in f: line = line.strip() if line.startswith("require"): key, value = line.split()[1].split("=", 1) build_info[key] = value f.close() bad = False for item in args.board_info_check: key, fn = item.split(":", 1) values = build_info.get(key, None) if not values: continue values = values.split("|") f = open(fn, "rb") digest = sha1(f.read()).hexdigest() f.close() versions = {} try: f = open(fn + ".sha1") except IOError: if not bad: print() print("*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key)) bad = True continue for line in f: line = line.strip() if not line or line.startswith("#"): continue h, v = line.split() versions[h] = v if digest not in versions: if not bad: print() print("*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn)) bad = True continue if versions[digest] not in values: if not bad: print() print("*** \"%s\" is version %s; not any %s allowed by \"%s\"." % ( fn, versions[digest], key, args.board_info_txt)) bad = True if bad: print() sys.exit(1) ================================================ FILE: tools/compare_builds.py ================================================ #!/usr/bin/env -S python3 -u """ This script helps find various build behaviors that make builds less hermetic and repeatable. Depending on the flags, it runs a sequence of builds and looks for files that have changed or have been improperly regenerated, updating their timestamps incorrectly. It also looks for changes that the build has done to the source tree, and for files whose contents are dependent on the location of the out directory. This utility has two major modes, full and incremental. By default, this tool runs in full mode. To run in incremental mode, pass the --incremental flag. FULL MODE In full mode, this tool helps verify BUILD CORRECTNESS by examining its REPEATABILITY. In full mode, this tool runs two complete builds in different directories and compares the CONTENTS of the two directories. Lists of any files that are added, removed or changed are printed, sorted by the timestamp of that file, to aid finding which dependencies trigger the rebuilding of other files. INCREMENTAL MODE In incremental mode, this tool helps verfiy the SPEED of the build. It runs two builds and looks at the TIMESTAMPS of the generated files, and reports files that were changed by the second build. In theory, an incremental build with no source files touched should not have any generated targets changed. As in full builds, the file list is returned sorted by timestamp. OTHER CHECKS In both full and incremental mode, this tool looks at the timestamps of all source files in the tree, and reports on files that have been touched. In the output, these are labeled with the header "Source files touched after start of build." In addition, by default, this tool sets the OUT_DIR environment variable to something other than "out" in order to find build rules that are not respecting the OUT_DIR. If you see these, you should fix them, but if your build can not complete for some reason because of this, you can pass the --no-check-out-dir flag to suppress this check. OTHER FLAGS In full mode, the --detect-embedded-paths flag does the two builds in different directories, to help in finding rules that embed the out directory path into the targets. The --hide-build-output flag hides the output of successful bulds, to make script output cleaner. The output of builds that fail is still shown. The --no-build flag is useful if you have already done a build and would just like to re-run the analysis. The --target flag lets you specify a build target other than the default full build (droid). You can pass "nothing" as in the example below, or a specific target, to reduce the scope of the checks performed. The --touch flag lets you specify a list of source files to touch between the builds, to examine the consequences of editing a particular file. EXAMPLE COMMANDLINES Please run build/make/tools/compare_builds.py --help for a full listing of the commandline flags. Here are a sampling of useful combinations. 1. Find files changed during an incremental build that doesn't build any targets. build/make/tools/compare_builds.py --incremental --target nothing Long incremental build times, or consecutive builds that re-run build actions are usually caused by files being touched as part of loading the makefiles. The nothing build (m nothing) loads the make and blueprint files, generates the dependency graph, but then doesn't actually build any targets. Checking against this build is the fastest and easiest way to find files that are modified while makefiles are read, for example with $(shell) invocations. 2. Find packaging targets that are different, ignoring intermediate files. build/make/tools/compare_builds.py --subdirs --detect-embedded-paths These flags will compare the final staging directories for partitions, as well as the APKs, apexes, testcases, and the like (the full directory list is in the DEFAULT_DIRS variable below). Since these are the files that are ultimately released, it is more important that these files be replicable, even if the intermediates that went into them are not (for example, when debugging symbols are stripped). 3. Check that all targets are repeatable. build/make/tools/compare_builds.py --detect-embedded-paths This check will list all of the differences in built targets that it can find. Be aware that the AOSP tree still has quite a few targets that are flagged by this check, so OEM changes might be lost in that list. That said, each file shown here is a potential blocker for a repeatable build. 4. See what targets are rebuilt when a file is touched between builds. build/make/tools/compare_builds.py --incremental \ --touch frameworks/base/core/java/android/app/Activity.java This check simulates the common engineer workflow of touching a single file and rebuilding the whole system. To see a restricted view, consider also passing a --target option for a common use case. For example: build/make/tools/compare_builds.py --incremental --target framework \ --touch frameworks/base/core/java/android/app/Activity.java """ import argparse import itertools import os import shutil import stat import subprocess import sys # Soong SOONG_UI = "build/soong/soong_ui.bash" # Which directories to use if no --subdirs is supplied without explicit directories. DEFAULT_DIRS = ( "apex", "data", "product", "ramdisk", "recovery", "root", "system", "system_ext", "system_other", "testcases", "vendor", ) # Files to skip for incremental timestamp checking BUILD_INTERNALS_PREFIX_SKIP = ( "soong/.glob/", ".path/", ) BUILD_INTERNALS_SUFFIX_SKIP = ( "/soong/soong_build_metrics.pb", "/.installable_test_files", "/files.db", "/.blueprint.bootstrap", "/build_number.txt", "/build.ninja", "/.out-dir", "/build_fingerprint.txt", "/build_thumbprint.txt", "/.copied_headers_list", "/.installable_files", ) class DiffType(object): def __init__(self, code, message): self.code = code self.message = message DIFF_NONE = DiffType("DIFF_NONE", "Files are the same") DIFF_MODE = DiffType("DIFF_MODE", "Stat mode bits differ") DIFF_SIZE = DiffType("DIFF_SIZE", "File size differs") DIFF_SYMLINK = DiffType("DIFF_SYMLINK", "Symlinks point to different locations") DIFF_CONTENTS = DiffType("DIFF_CONTENTS", "File contents differ") def main(): argparser = argparse.ArgumentParser(description="Diff build outputs from two builds.", epilog="Run this command from the root of the tree." + " Before running this command, the build environment" + " must be set up, including sourcing build/envsetup.sh" + " and running lunch.") argparser.add_argument("--detect-embedded-paths", action="store_true", help="Use unique out dirs to detect paths embedded in binaries.") argparser.add_argument("--incremental", action="store_true", help="Compare which files are touched in two consecutive builds without a clean in between.") argparser.add_argument("--hide-build-output", action="store_true", help="Don't print the build output for successful builds") argparser.add_argument("--no-build", dest="run_build", action="store_false", help="Don't build or clean, but do everything else.") argparser.add_argument("--no-check-out-dir", dest="check_out_dir", action="store_false", help="Don't check for rules not honoring movable out directories.") argparser.add_argument("--subdirs", nargs="*", help="Only scan these subdirs of $PRODUCT_OUT instead of the whole out directory." + " The --subdirs argument with no listed directories will give a default list.") argparser.add_argument("--target", default="droid", help="Make target to run. The default is droid") argparser.add_argument("--touch", nargs="+", default=[], help="Files to touch between builds. Must pair with --incremental.") args = argparser.parse_args(sys.argv[1:]) if args.detect_embedded_paths and args.incremental: sys.stderr.write("Can't pass --detect-embedded-paths and --incremental together.\n") sys.exit(1) if args.detect_embedded_paths and not args.check_out_dir: sys.stderr.write("Can't pass --detect-embedded-paths and --no-check-out-dir together.\n") sys.exit(1) if args.touch and not args.incremental: sys.stderr.write("The --incremental flag is required if the --touch flag is passed.") sys.exit(1) AssertAtTop() RequireEnvVar("TARGET_PRODUCT") RequireEnvVar("TARGET_BUILD_VARIANT") # Out dir file names: # - dir_prefix - The directory we'll put everything in (except for maybe the top level # out/ dir). # - *work_dir - The directory that we will build directly into. This is in dir_prefix # unless --no-check-out-dir is set. # - *out_dir - After building, if work_dir is different from out_dir, we move the out # directory to here so we can do the comparisions. # - timestamp_* - Files we touch so we know the various phases between the builds, so we # can compare timestamps of files. if args.incremental: dir_prefix = "out_incremental" if args.check_out_dir: first_work_dir = first_out_dir = dir_prefix + "/out" second_work_dir = second_out_dir = dir_prefix + "/out" else: first_work_dir = first_out_dir = "out" second_work_dir = second_out_dir = "out" else: dir_prefix = "out_full" first_out_dir = dir_prefix + "/out_1" second_out_dir = dir_prefix + "/out_2" if not args.check_out_dir: first_work_dir = second_work_dir = "out" elif args.detect_embedded_paths: first_work_dir = first_out_dir second_work_dir = second_out_dir else: first_work_dir = dir_prefix + "/work" second_work_dir = dir_prefix + "/work" timestamp_start = dir_prefix + "/timestamp_start" timestamp_between = dir_prefix + "/timestamp_between" timestamp_end = dir_prefix + "/timestamp_end" if args.run_build: # Initial clean, if necessary print("Cleaning " + dir_prefix + "/") Clean(dir_prefix) print("Cleaning out/") Clean("out") CreateEmptyFile(timestamp_start) print("Running the first build in " + first_work_dir) RunBuild(first_work_dir, first_out_dir, args.target, args.hide_build_output) for f in args.touch: print("Touching " + f) TouchFile(f) CreateEmptyFile(timestamp_between) print("Running the second build in " + second_work_dir) RunBuild(second_work_dir, second_out_dir, args.target, args.hide_build_output) CreateEmptyFile(timestamp_end) print("Done building") print() # Which out directories to scan if args.subdirs is not None: if args.subdirs: subdirs = args.subdirs else: subdirs = DEFAULT_DIRS first_files = ProductFiles(RequireBuildVar(first_out_dir, "PRODUCT_OUT"), subdirs) second_files = ProductFiles(RequireBuildVar(second_out_dir, "PRODUCT_OUT"), subdirs) else: first_files = OutFiles(first_out_dir) second_files = OutFiles(second_out_dir) printer = Printer() if args.incremental: # Find files that were rebuilt unnecessarily touched_incrementally = FindOutFilesTouchedAfter(first_files, GetFileTimestamp(timestamp_between)) printer.PrintList("Touched in incremental build", touched_incrementally) else: # Compare the two out dirs added, removed, changed = DiffFileList(first_files, second_files) printer.PrintList("Added", added) printer.PrintList("Removed", removed) printer.PrintList("Changed", changed, "%s %s") # Find files in the source tree that were touched touched_during = FindSourceFilesTouchedAfter(GetFileTimestamp(timestamp_start)) printer.PrintList("Source files touched after start of build", touched_during) # Find files and dirs that were output to "out" and didn't respect $OUT_DIR if args.check_out_dir: bad_out_dir_contents = FindFilesAndDirectories("out") printer.PrintList("Files and directories created by rules that didn't respect $OUT_DIR", bad_out_dir_contents) # If we didn't find anything, print success message if not printer.printed_anything: print("No bad behaviors found.") def AssertAtTop(): """If the current directory is not the top of an android source tree, print an error message and exit.""" if not os.access(SOONG_UI, os.X_OK): sys.stderr.write("FAILED: Please run from the root of the tree.\n") sys.exit(1) def RequireEnvVar(name): """Gets an environment variable. If that fails, then print an error message and exit.""" result = os.environ.get(name) if not result: sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) sys.exit(1) return result def RunSoong(out_dir, args, capture_output): env = dict(os.environ) env["OUT_DIR"] = out_dir args = [SOONG_UI,] + args if capture_output: proc = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) combined_output, none = proc.communicate() return proc.returncode, combined_output else: result = subprocess.run(args, env=env) return result.returncode, None def GetBuildVar(out_dir, name): """Gets a variable from the build system.""" returncode, output = RunSoong(out_dir, ["--dumpvar-mode", name], True) if returncode != 0: return None else: return output.decode("utf-8").strip() def RequireBuildVar(out_dir, name): """Gets a variable from the builds system. If that fails, then print an error message and exit.""" value = GetBuildVar(out_dir, name) if not value: sys.stderr.write("error: Can't determine %s. Please run lunch first.\n" % name) sys.exit(1) return value def Clean(directory): """"Deletes the supplied directory.""" try: shutil.rmtree(directory) except FileNotFoundError: pass def RunBuild(work_dir, out_dir, target, hide_build_output): """Runs a build. If the build fails, prints a message and exits.""" returncode, output = RunSoong(work_dir, ["--build-mode", "--all-modules", "--dir=" + os.getcwd(), target], hide_build_output) if work_dir != out_dir: os.replace(work_dir, out_dir) if returncode != 0: if hide_build_output: # The build output was hidden, so print it now for debugging sys.stderr.buffer.write(output) sys.stderr.write("FAILED: Build failed. Stopping.\n") sys.exit(1) def DiffFileList(first_files, second_files): """Examines the files. Returns: Filenames of files in first_filelist but not second_filelist (added files) Filenames of files in second_filelist but not first_filelist (removed files) 2-Tuple of filenames for the files that are in both but are different (changed files) """ # List of files, relative to their respective PRODUCT_OUT directories first_filelist = sorted([x for x in first_files], key=lambda x: x[1]) second_filelist = sorted([x for x in second_files], key=lambda x: x[1]) added = [] removed = [] changed = [] first_index = 0 second_index = 0 while first_index < len(first_filelist) and second_index < len(second_filelist): # Path relative to source root and path relative to PRODUCT_OUT first_full_filename, first_relative_filename = first_filelist[first_index] second_full_filename, second_relative_filename = second_filelist[second_index] if first_relative_filename < second_relative_filename: # Removed removed.append(first_full_filename) first_index += 1 elif first_relative_filename > second_relative_filename: # Added added.append(second_full_filename) second_index += 1 else: # Both present diff_type = DiffFiles(first_full_filename, second_full_filename) if diff_type != DIFF_NONE: changed.append((first_full_filename, second_full_filename)) first_index += 1 second_index += 1 while first_index < len(first_filelist): first_full_filename, first_relative_filename = first_filelist[first_index] removed.append(first_full_filename) first_index += 1 while second_index < len(second_filelist): second_full_filename, second_relative_filename = second_filelist[second_index] added.append(second_full_filename) second_index += 1 return (SortByTimestamp(added), SortByTimestamp(removed), SortByTimestamp(changed, key=lambda item: item[1])) def FindOutFilesTouchedAfter(files, timestamp): """Find files in the given file iterator that were touched after timestamp.""" result = [] for full, relative in files: ts = GetFileTimestamp(full) if ts > timestamp: result.append(TouchedFile(full, ts)) return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] def GetFileTimestamp(filename): """Get timestamp for a file (just wraps stat).""" st = os.stat(filename, follow_symlinks=False) return st.st_mtime def SortByTimestamp(items, key=lambda item: item): """Sort the list by timestamp of files. Args: items - the list of items to sort key - a function to extract a filename from each element in items """ return [x[0] for x in sorted([(item, GetFileTimestamp(key(item))) for item in items], key=lambda y: y[1])] def FindSourceFilesTouchedAfter(timestamp): """Find files in the source tree that have changed after timestamp. Ignores the out directory.""" result = [] for root, dirs, files in os.walk(".", followlinks=False): if root == ".": RemoveItemsFromList(dirs, (".repo", "out", "out_full", "out_incremental")) for f in files: full = os.path.sep.join((root, f))[2:] ts = GetFileTimestamp(full) if ts > timestamp: result.append(TouchedFile(full, ts)) return [f.filename for f in sorted(result, key=lambda f: f.timestamp)] def FindFilesAndDirectories(directory): """Finds all files and directories inside a directory.""" result = [] for root, dirs, files in os.walk(directory, followlinks=False): result += [os.path.sep.join((root, x, "")) for x in dirs] result += [os.path.sep.join((root, x)) for x in files] return result def CreateEmptyFile(filename): """Create an empty file with now as the timestamp at filename.""" try: os.makedirs(os.path.dirname(filename)) except FileExistsError: pass open(filename, "w").close() os.utime(filename) def TouchFile(filename): os.utime(filename) def DiffFiles(first_filename, second_filename): def AreFileContentsSame(remaining, first_filename, second_filename): """Compare the file contents. They must be known to be the same size.""" CHUNK_SIZE = 32*1024 with open(first_filename, "rb") as first_file: with open(second_filename, "rb") as second_file: while remaining > 0: size = min(CHUNK_SIZE, remaining) if first_file.read(CHUNK_SIZE) != second_file.read(CHUNK_SIZE): return False remaining -= size return True first_stat = os.stat(first_filename, follow_symlinks=False) second_stat = os.stat(first_filename, follow_symlinks=False) # Mode bits if first_stat.st_mode != second_stat.st_mode: return DIFF_MODE # File size if first_stat.st_size != second_stat.st_size: return DIFF_SIZE # Contents if stat.S_ISLNK(first_stat.st_mode): if os.readlink(first_filename) != os.readlink(second_filename): return DIFF_SYMLINK elif stat.S_ISREG(first_stat.st_mode): if not AreFileContentsSame(first_stat.st_size, first_filename, second_filename): return DIFF_CONTENTS return DIFF_NONE class FileIterator(object): """Object that produces an iterator containing all files in a given directory. Each iteration yields a tuple containing: [0] (full) Path to file relative to source tree. [1] (relative) Path to the file relative to the base directory given in the constructor. """ def __init__(self, base_dir): self._base_dir = base_dir def __iter__(self): return self._Iterator(self, self._base_dir) def ShouldIncludeFile(self, root, path): return False class _Iterator(object): def __init__(self, parent, base_dir): self._parent = parent self._base_dir = base_dir self._walker = os.walk(base_dir, followlinks=False) self._current_index = 0 self._current_dir = [] def __iter__(self): return self def __next__(self): # os.walk's iterator will eventually terminate by raising StopIteration while True: if self._current_index >= len(self._current_dir): root, dirs, files = self._walker.__next__() full_paths = [os.path.sep.join((root, f)) for f in files] pairs = [(f, f[len(self._base_dir)+1:]) for f in full_paths] self._current_dir = [(full, relative) for full, relative in pairs if self._parent.ShouldIncludeFile(root, relative)] self._current_index = 0 if not self._current_dir: continue index = self._current_index self._current_index += 1 return self._current_dir[index] class OutFiles(FileIterator): """Object that produces an iterator containing all files in a given out directory, except for files which are known to be touched as part of build setup. """ def __init__(self, out_dir): super().__init__(out_dir) self._out_dir = out_dir def ShouldIncludeFile(self, root, relative): # Skip files in root, although note that this could actually skip # files that are sadly generated directly into that directory. if root == self._out_dir: return False # Skiplist for skip in BUILD_INTERNALS_PREFIX_SKIP: if relative.startswith(skip): return False for skip in BUILD_INTERNALS_SUFFIX_SKIP: if relative.endswith(skip): return False return True class ProductFiles(FileIterator): """Object that produces an iterator containing files in listed subdirectories of $PRODUCT_OUT. """ def __init__(self, product_out, subdirs): super().__init__(product_out) self._subdirs = subdirs def ShouldIncludeFile(self, root, relative): for subdir in self._subdirs: if relative.startswith(subdir): return True return False class TouchedFile(object): """A file in the out directory with a timestamp.""" def __init__(self, filename, timestamp): self.filename = filename self.timestamp = timestamp def RemoveItemsFromList(haystack, needles): for needle in needles: try: haystack.remove(needle) except ValueError: pass class Printer(object): def __init__(self): self.printed_anything = False def PrintList(self, title, items, fmt="%s"): if items: if self.printed_anything: sys.stdout.write("\n") sys.stdout.write("%s:\n" % title) for item in items: sys.stdout.write(" %s\n" % fmt % item) self.printed_anything = True if __name__ == "__main__": try: main() except KeyboardInterrupt: pass # vim: ts=2 sw=2 sts=2 nocindent ================================================ FILE: tools/compliance/Android.bp ================================================ // // Copyright (C) 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { default_applicable_licenses: ["Android-Apache-2.0"], } blueprint_go_binary { name: "compliance_checkmetadata", srcs: ["cmd/checkmetadata/checkmetadata.go"], deps: [ "compliance-module", "projectmetadata-module", "soong-response", ], testSrcs: ["cmd/checkmetadata/checkmetadata_test.go"], } blueprint_go_binary { name: "compliance_checkshare", srcs: ["cmd/checkshare/checkshare.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/checkshare/checkshare_test.go"], } blueprint_go_binary { name: "compliancenotice_shippedlibs", srcs: ["cmd/shippedlibs/shippedlibs.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/shippedlibs/shippedlibs_test.go"], } blueprint_go_binary { name: "compliance_listshare", srcs: ["cmd/listshare/listshare.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/listshare/listshare_test.go"], } blueprint_go_binary { name: "compliance_dumpgraph", srcs: ["cmd/dumpgraph/dumpgraph.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/dumpgraph/dumpgraph_test.go"], } blueprint_go_binary { name: "compliance_dumpresolutions", srcs: ["cmd/dumpresolutions/dumpresolutions.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/dumpresolutions/dumpresolutions_test.go"], } blueprint_go_binary { name: "htmlnotice", srcs: ["cmd/htmlnotice/htmlnotice.go"], deps: [ "compliance-module", "blueprint-deptools", "soong-response", ], testSrcs: ["cmd/htmlnotice/htmlnotice_test.go"], } blueprint_go_binary { name: "compliance_rtrace", srcs: ["cmd/rtrace/rtrace.go"], deps: [ "compliance-module", "soong-response", ], testSrcs: ["cmd/rtrace/rtrace_test.go"], } blueprint_go_binary { name: "textnotice", srcs: ["cmd/textnotice/textnotice.go"], deps: [ "compliance-module", "blueprint-deptools", "soong-response", ], testSrcs: ["cmd/textnotice/textnotice_test.go"], } blueprint_go_binary { name: "xmlnotice", srcs: ["cmd/xmlnotice/xmlnotice.go"], deps: [ "compliance-module", "blueprint-deptools", "soong-response", ], testSrcs: ["cmd/xmlnotice/xmlnotice_test.go"], } bootstrap_go_package { name: "compliance-module", srcs: [ "condition.go", "conditionset.go", "doc.go", "graph.go", "noticeindex.go", "policy_policy.go", "policy_resolve.go", "policy_resolvenotices.go", "policy_resolveshare.go", "policy_resolveprivacy.go", "policy_shareprivacyconflicts.go", "policy_shipped.go", "policy_walk.go", "readgraph.go", "resolution.go", "resolutionset.go", ], testSrcs: [ "condition_test.go", "conditionset_test.go", "readgraph_test.go", "policy_policy_test.go", "policy_resolve_test.go", "policy_resolvenotices_test.go", "policy_resolveshare_test.go", "policy_resolveprivacy_test.go", "policy_shareprivacyconflicts_test.go", "policy_shipped_test.go", "policy_walk_test.go", "resolutionset_test.go", "test_util.go", ], deps: [ "compliance-test-fs-module", "projectmetadata-module", "golang-protobuf-proto", "golang-protobuf-encoding-prototext", "license_metadata_proto", ], pkgPath: "android/soong/tools/compliance", } ================================================ FILE: tools/compliance/README.md ================================================ # Compliance Package compliance provides an approved means for reading, consuming, and analyzing license metadata graphs. Assuming the license metadata and dependencies are fully and accurately recorded in the build system, any discrepancy between the official policy for open source license compliance and this code is **a bug in this code.** ## Naming All of the code that directly reflects a policy decision belongs in a file with a name begninning `policy_`. Changes to these files need to be authored or reviewed by someone in OSPO or whichever successor group governs policy. The files with names not beginning `policy_` describe data types, and general, reusable algorithms. The source code for binary tools and utilities appears under the `cmd/` subdirectory. Other subdirectories contain reusable components that are not `compliance` per se. ## Data Types A few principal types to understand are LicenseGraph, LicenseCondition, and ResolutionSet. ### LicenseGraph A LicenseGraph is an immutable graph of the targets and dependencies reachable from a specific set of root targets. In general, the root targets will be the artifacts in a release or distribution. While conceptually immutable, parts of the graph may be loaded or evaluated lazily. Conceptually, the graph itself will always be a directed acyclic graph. One representation is a set of directed edges. Another is a set of nodes with directed edges to their dependencies. The edges have annotations, which can distinguish between build tools, runtime dependencies, and dependencies like 'contains' that make a derivative work. ### LicenseCondition A LicenseCondition is an immutable tuple pairing a condition name with an originating target. e.g. Per current policy, a static library licensed under an MIT license would pair a "notice" condition with the static library target, and a dynamic license licensed under GPL would pair a "restricted" condition with the dynamic library target. ### ResolutionSet A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves` tuples describing how license conditions apply to targets. `AttachesTo` is the trigger for acting. Distribution of the target invokes the policy. `ActsOn` is the target to share, give notice for, hide etc. `Resolves` is the set of conditions that the action resolves. For most condition types, `ActsOn` will be the target where the condition originated. For example, a notice condition policy means attribution or notice must be given for the target where the condition originates. Likewise, a proprietary condition policy means the privacy of the target where the condition originates must be respected. i.e. The thing acted on is the origin. Restricted conditions are different. The infectious nature of restricted often means sharing code that is not the target where the restricted condition originates. Linking an MIT library to a GPL library implies a policy to share the MIT library despite the MIT license having no source sharing requirement. In this case, one or more resolution tuples will have the MIT license module in `ActsOn` and the restricted condition originating at the GPL library module in `Resolves`. These tuples will `AttachTo` every target that depends on the GPL library because shipping any of those targets trigger the policy to share the code. ## Processes ### ReadLicenseGraph The principal means to ingest license metadata. Given the distribution targets, ReadLicenseGraph populates the LicenseGraph for those root targets. ### NoticeIndex.IndexLicenseTexts IndexLicenseTexts reads, deduplicates and caches license texts for notice files. Also reads and caches project metadata for deriving library names. The algorithm for deriving library names has not been dictated by OSPO policy, but reflects a pragmatic attempt to comply with Android policy regarding unreleased product names, proprietary partner names etc. ### projectmetadata.Index.MetadataForProjects MetadataForProjects reads, deduplicates and caches project METADATA files used for notice library names, and various properties appearing in SBOMs. ================================================ FILE: tools/compliance/cmd/checkmetadata/checkmetadata.go ================================================ // Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "flag" "fmt" "io" "io/fs" "os" "path/filepath" "strings" "android/soong/response" "android/soong/tools/compliance" "android/soong/tools/compliance/projectmetadata" ) var ( failNoneRequested = fmt.Errorf("\nNo projects requested") ) func main() { var expandedArgs []string for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "@") { f, err := os.Open(strings.TrimPrefix(arg, "@")) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } respArgs, err := response.ReadRspFile(f) f.Close() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } expandedArgs = append(expandedArgs, respArgs...) } else { expandedArgs = append(expandedArgs, arg) } } flags := flag.NewFlagSet("flags", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} projectdir {projectdir...} Tries to open the METADATA.android or METADATA file in each projectdir reporting any errors on stderr. Reports "FAIL" to stdout if any errors found and exits with status 1. Otherwise, reports "PASS" and the number of project metadata files found exiting with status 0. `, filepath.Base(os.Args[0])) flags.PrintDefaults() } outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") flags.Parse(expandedArgs) // Must specify at least one root target. if flags.NArg() == 0 { flags.Usage() os.Exit(2) } if len(*outputFile) == 0 { flags.Usage() fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") os.Exit(2) } else { dir, err := filepath.Abs(filepath.Dir(*outputFile)) if err != nil { fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) os.Exit(1) } fi, err := os.Stat(dir) if err != nil { fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) os.Exit(1) } if !fi.IsDir() { fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) os.Exit(1) } } var ofile io.Writer ofile = os.Stdout var obuf *bytes.Buffer if *outputFile != "-" { obuf = &bytes.Buffer{} ofile = obuf } err := checkProjectMetadata(ofile, os.Stderr, compliance.FS, flags.Args()...) if err != nil { if err == failNoneRequested { flags.Usage() } fmt.Fprintf(os.Stderr, "%s\n", err.Error()) fmt.Fprintln(ofile, "FAIL") os.Exit(1) } if *outputFile != "-" { err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) if err != nil { fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) os.Exit(1) } } os.Exit(0) } // checkProjectMetadata implements the checkmetadata utility. func checkProjectMetadata(stdout, stderr io.Writer, rootFS fs.FS, projects ...string) error { if len(projects) < 1 { return failNoneRequested } // Read the project metadata files from `projects` ix := projectmetadata.NewIndex(rootFS) pms, err := ix.MetadataForProjects(projects...) if err != nil { return fmt.Errorf("Unable to read project metadata file(s) %q from %q: %w\n", projects, os.Getenv("PWD"), err) } fmt.Fprintf(stdout, "PASS -- parsed %d project metadata files for %d projects\n", len(pms), len(projects)) return nil } ================================================ FILE: tools/compliance/cmd/checkmetadata/checkmetadata_test.go ================================================ // Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "fmt" "os" "strings" "testing" "android/soong/tools/compliance" ) func TestMain(m *testing.M) { // Change into the parent directory before running the tests // so they can find the testdata directory. if err := os.Chdir(".."); err != nil { fmt.Printf("failed to change to testdata directory: %s\n", err) os.Exit(1) } os.Exit(m.Run()) } func Test(t *testing.T) { tests := []struct { name string projects []string expectedStdout string }{ { name: "1p", projects: []string{"firstparty"}, expectedStdout: "PASS -- parsed 1 project metadata files for 1 projects", }, { name: "notice", projects: []string{"notice"}, expectedStdout: "PASS -- parsed 1 project metadata files for 1 projects", }, { name: "1p+notice", projects: []string{"firstparty", "notice"}, expectedStdout: "PASS -- parsed 2 project metadata files for 2 projects", }, { name: "reciprocal", projects: []string{"reciprocal"}, expectedStdout: "PASS -- parsed 1 project metadata files for 1 projects", }, { name: "1p+notice+reciprocal", projects: []string{"firstparty", "notice", "reciprocal"}, expectedStdout: "PASS -- parsed 3 project metadata files for 3 projects", }, { name: "restricted", projects: []string{"restricted"}, expectedStdout: "PASS -- parsed 1 project metadata files for 1 projects", }, { name: "1p+notice+reciprocal+restricted", projects: []string{ "firstparty", "notice", "reciprocal", "restricted", }, expectedStdout: "PASS -- parsed 4 project metadata files for 4 projects", }, { name: "proprietary", projects: []string{"proprietary"}, expectedStdout: "PASS -- parsed 1 project metadata files for 1 projects", }, { name: "1p+notice+reciprocal+restricted+proprietary", projects: []string{ "firstparty", "notice", "reciprocal", "restricted", "proprietary", }, expectedStdout: "PASS -- parsed 5 project metadata files for 5 projects", }, { name: "missing1", projects: []string{"regressgpl1"}, expectedStdout: "PASS -- parsed 0 project metadata files for 1 projects", }, { name: "1p+notice+reciprocal+restricted+proprietary+missing1", projects: []string{ "firstparty", "notice", "reciprocal", "restricted", "proprietary", "regressgpl1", }, expectedStdout: "PASS -- parsed 5 project metadata files for 6 projects", }, { name: "missing2", projects: []string{"regressgpl2"}, expectedStdout: "PASS -- parsed 0 project metadata files for 1 projects", }, { name: "1p+notice+reciprocal+restricted+proprietary+missing1+missing2", projects: []string{ "firstparty", "notice", "reciprocal", "restricted", "proprietary", "regressgpl1", "regressgpl2", }, expectedStdout: "PASS -- parsed 5 project metadata files for 7 projects", }, { name: "missing2+1p+notice+reciprocal+restricted+proprietary+missing1", projects: []string{ "regressgpl2", "firstparty", "notice", "reciprocal", "restricted", "proprietary", "regressgpl1", }, expectedStdout: "PASS -- parsed 5 project metadata files for 7 projects", }, { name: "missing2+1p+notice+missing1+reciprocal+restricted+proprietary", projects: []string{ "regressgpl2", "firstparty", "notice", "regressgpl1", "reciprocal", "restricted", "proprietary", }, expectedStdout: "PASS -- parsed 5 project metadata files for 7 projects", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} projects := make([]string, 0, len(tt.projects)) for _, project := range tt.projects { projects = append(projects, "testdata/"+project) } err := checkProjectMetadata(stdout, stderr, compliance.GetFS(""), projects...) if err != nil { t.Fatalf("checkmetadata: error = %v, stderr = %v", err, stderr) return } var actualStdout string for _, s := range strings.Split(stdout.String(), "\n") { ts := strings.TrimLeft(s, " \t") if len(ts) < 1 { continue } if len(actualStdout) > 0 { t.Errorf("checkmetadata: unexpected multiple output lines %q, want %q", actualStdout+"\n"+ts, tt.expectedStdout) } actualStdout = ts } if actualStdout != tt.expectedStdout { t.Errorf("checkmetadata: unexpected stdout %q, want %q", actualStdout, tt.expectedStdout) } }) } } ================================================ FILE: tools/compliance/cmd/checkshare/checkshare.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "flag" "fmt" "io" "io/fs" "os" "path/filepath" "sort" "strings" "android/soong/response" "android/soong/tools/compliance" ) var ( failConflicts = fmt.Errorf("conflicts") failNoneRequested = fmt.Errorf("\nNo metadata files requested") failNoLicenses = fmt.Errorf("No licenses") ) // byError orders conflicts by error string type byError []compliance.SourceSharePrivacyConflict func (l byError) Len() int { return len(l) } func (l byError) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() } func main() { var expandedArgs []string for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "@") { f, err := os.Open(strings.TrimPrefix(arg, "@")) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } respArgs, err := response.ReadRspFile(f) f.Close() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } expandedArgs = append(expandedArgs, respArgs...) } else { expandedArgs = append(expandedArgs, arg) } } flags := flag.NewFlagSet("flags", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...} Reports on stderr any targets where policy says that the source both must and must not be shared. The error report indicates the target, the license condition that has a source privacy policy, and the license condition that has a source sharing policy. Any given target may appear multiple times with different combinations of conflicting license conditions. If all the source code that policy says must be shared may be shared, outputs "PASS" to stdout and exits with status 0. If policy says any source must both be shared and not be shared, outputs "FAIL" to stdout and exits with status 1. `, filepath.Base(os.Args[0])) flags.PrintDefaults() } outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") flags.Parse(expandedArgs) // Must specify at least one root target. if flags.NArg() == 0 { flags.Usage() os.Exit(2) } if len(*outputFile) == 0 { flags.Usage() fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") os.Exit(2) } else { dir, err := filepath.Abs(filepath.Dir(*outputFile)) if err != nil { fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) os.Exit(1) } fi, err := os.Stat(dir) if err != nil { fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) os.Exit(1) } if !fi.IsDir() { fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) os.Exit(1) } } var ofile io.Writer ofile = os.Stdout var obuf *bytes.Buffer if *outputFile != "-" { obuf = &bytes.Buffer{} ofile = obuf } err := checkShare(ofile, os.Stderr, compliance.FS, flags.Args()...) if err != nil { if err != failConflicts { if err == failNoneRequested { flags.Usage() } fmt.Fprintf(os.Stderr, "%s\n", err.Error()) } os.Exit(1) } if *outputFile != "-" { err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) if err != nil { fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) os.Exit(1) } } os.Exit(0) } // checkShare implements the checkshare utility. func checkShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error { if len(files) < 1 { return failNoneRequested } // Read the license graph from the license metadata files (*.meta_lic). licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) if err != nil { return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %w\n", files, os.Getenv("PWD"), err) } if licenseGraph == nil { return failNoLicenses } // Apply policy to find conflicts and report them to stderr lexicographically ordered. conflicts := compliance.ConflictingSharedPrivateSource(licenseGraph) sort.Sort(byError(conflicts)) for _, conflict := range conflicts { fmt.Fprintln(stderr, conflict.Error()) } // Indicate pass or fail on stdout. if len(conflicts) > 0 { fmt.Fprintln(stdout, "FAIL") return failConflicts } fmt.Fprintln(stdout, "PASS") return nil } ================================================ FILE: tools/compliance/cmd/checkshare/checkshare_test.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "fmt" "os" "strings" "testing" "android/soong/tools/compliance" ) func TestMain(m *testing.M) { // Change into the parent directory before running the tests // so they can find the testdata directory. if err := os.Chdir(".."); err != nil { fmt.Printf("failed to change to testdata directory: %s\n", err) os.Exit(1) } os.Exit(m.Run()) } type outcome struct { target string privacyCondition string shareCondition string } func (o *outcome) String() string { return fmt.Sprintf("%s %s and must share from %s", o.target, o.privacyCondition, o.shareCondition) } type outcomeList []*outcome func (ol outcomeList) String() string { result := "" for _, o := range ol { result = result + o.String() + "\n" } return result } func Test(t *testing.T) { tests := []struct { condition string name string outDir string roots []string expectedStdout string expectedOutcomes outcomeList }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedStdout: "PASS", }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "PASS", }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedStdout: "PASS", }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin2.meta_lic"}, expectedStdout: "PASS", }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedStdout: "PASS", }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedStdout: "PASS", }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "PASS", }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedStdout: "PASS", }, { condition: "notice", name: "binary", roots: []string{"bin/bin2.meta_lic"}, expectedStdout: "PASS", }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedStdout: "PASS", }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedStdout: "PASS", }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "PASS", }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedStdout: "PASS", }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin2.meta_lic"}, expectedStdout: "PASS", }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedStdout: "PASS", }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedStdout: "PASS", }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "PASS", }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedStdout: "PASS", }, { condition: "restricted", name: "binary", roots: []string{"bin/bin2.meta_lic"}, expectedStdout: "PASS", }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedStdout: "PASS", }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedStdout: "FAIL", expectedOutcomes: outcomeList{ &outcome{ target: "testdata/proprietary/bin/bin2.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "FAIL", expectedOutcomes: outcomeList{ &outcome{ target: "testdata/proprietary/bin/bin2.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedStdout: "FAIL", expectedOutcomes: outcomeList{ &outcome{ target: "testdata/proprietary/lib/liba.so.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin2.meta_lic", "lib/libb.so.meta_lic"}, expectedStdout: "FAIL", expectedOutcomes: outcomeList{ &outcome{ target: "testdata/proprietary/bin/bin2.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedStdout: "PASS", }, { condition: "regressconcur", name: "container", roots: []string{"container.zip.meta_lic"}, expectedStdout: "FAIL", expectedOutcomes: outcomeList{ &outcome{ target: "testdata/regressconcur/bin/bin1.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin2.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin3.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin4.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin5.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin6.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin7.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin8.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, &outcome{ target: "testdata/regressconcur/bin/bin9.meta_lic", privacyCondition: "proprietary", shareCondition: "restricted", }, }, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } err := checkShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) if err != nil && err != failConflicts { t.Fatalf("checkshare: error = %v, stderr = %v", err, stderr) return } var actualStdout string for _, s := range strings.Split(stdout.String(), "\n") { ts := strings.TrimLeft(s, " \t") if len(ts) < 1 { continue } if len(actualStdout) > 0 { t.Errorf("checkshare: unexpected multiple output lines %q, want %q", actualStdout+"\n"+ts, tt.expectedStdout) } actualStdout = ts } if actualStdout != tt.expectedStdout { t.Errorf("checkshare: unexpected stdout %q, want %q", actualStdout, tt.expectedStdout) } errList := strings.Split(stderr.String(), "\n") actualOutcomes := make(outcomeList, 0, len(errList)) for _, cstring := range errList { ts := strings.TrimLeft(cstring, " \t") if len(ts) < 1 { continue } cFields := strings.Split(ts, " ") actualOutcomes = append(actualOutcomes, &outcome{ target: cFields[0], privacyCondition: cFields[1], shareCondition: cFields[6], }) } if len(actualOutcomes) != len(tt.expectedOutcomes) { t.Errorf("checkshare: unexpected got %d outcomes %s, want %d outcomes %s", len(actualOutcomes), actualOutcomes, len(tt.expectedOutcomes), tt.expectedOutcomes) return } for i := range actualOutcomes { if actualOutcomes[i].String() != tt.expectedOutcomes[i].String() { t.Errorf("checkshare: unexpected outcome #%d, got %q, want %q", i+1, actualOutcomes[i], tt.expectedOutcomes[i]) } } }) } } ================================================ FILE: tools/compliance/cmd/dumpgraph/dumpgraph.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "flag" "fmt" "io" "io/fs" "os" "path/filepath" "sort" "strings" "android/soong/response" "android/soong/tools/compliance" ) var ( failNoneRequested = fmt.Errorf("\nNo license metadata files requested") failNoLicenses = fmt.Errorf("No licenses found") ) type context struct { graphViz bool labelConditions bool stripPrefix []string } func (ctx context) strip(installPath string) string { for _, prefix := range ctx.stripPrefix { if strings.HasPrefix(installPath, prefix) { p := strings.TrimPrefix(installPath, prefix) if 0 == len(p) { continue } return p } } return installPath } // newMultiString creates a flag that allows multiple values in an array. func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { var f multiString flags.Var(&f, name, usage) return &f } // multiString implements the flag `Value` interface for multiple strings. type multiString []string func (ms *multiString) String() string { return strings.Join(*ms, ", ") } func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } func main() { var expandedArgs []string for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "@") { f, err := os.Open(strings.TrimPrefix(arg, "@")) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } respArgs, err := response.ReadRspFile(f) f.Close() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } expandedArgs = append(expandedArgs, respArgs...) } else { expandedArgs = append(expandedArgs, arg) } } flags := flag.NewFlagSet("flags", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} Outputs space-separated Target Dependency Annotations tuples for each edge in the license graph. When -dot flag given, outputs the nodes and edges in graphViz directed graph format. In plain text mode, multiple values within a field are colon-separated. e.g. multiple annotations appear as annotation1:annotation2:annotation3 or when -label_conditions is requested, Target and Dependency become target:condition1:condition2 etc. Options: `, filepath.Base(os.Args[0])) flags.PrintDefaults() } graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.") labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.") outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") flags.Parse(expandedArgs) // Must specify at least one root target. if flags.NArg() == 0 { flags.Usage() os.Exit(2) } if len(*outputFile) == 0 { flags.Usage() fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") os.Exit(2) } else { dir, err := filepath.Abs(filepath.Dir(*outputFile)) if err != nil { fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) os.Exit(1) } fi, err := os.Stat(dir) if err != nil { fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) os.Exit(1) } if !fi.IsDir() { fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) os.Exit(1) } } var ofile io.Writer ofile = os.Stdout var obuf *bytes.Buffer if *outputFile != "-" { obuf = &bytes.Buffer{} ofile = obuf } ctx := &context{*graphViz, *labelConditions, *stripPrefix} err := dumpGraph(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...) if err != nil { if err == failNoneRequested { flags.Usage() } fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) } if *outputFile != "-" { err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) if err != nil { fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) os.Exit(1) } } os.Exit(0) } // dumpGraph implements the dumpgraph utility. func dumpGraph(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) error { if len(files) < 1 { return failNoneRequested } // Read the license graph from the license metadata files (*.meta_lic). licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) if err != nil { return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err) } if licenseGraph == nil { return failNoLicenses } // Sort the edges of the graph. edges := licenseGraph.Edges() sort.Sort(edges) // nodes maps license metadata file names to graphViz node names when ctx.graphViz is true. var nodes map[string]string n := 0 // targetOut calculates the string to output for `target` separating conditions as needed using `sep`. targetOut := func(target *compliance.TargetNode, sep string) string { tOut := ctx.strip(target.Name()) if ctx.labelConditions { conditions := target.LicenseConditions().Names() sort.Strings(conditions) if len(conditions) > 0 { tOut += sep + strings.Join(conditions, sep) } } return tOut } // makeNode maps `target` to a graphViz node name. makeNode := func(target *compliance.TargetNode) { tName := target.Name() if _, ok := nodes[tName]; !ok { nodeName := fmt.Sprintf("n%d", n) nodes[tName] = nodeName fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n")) n++ } } // If graphviz output, map targets to node names, and start the directed graph. if ctx.graphViz { nodes = make(map[string]string) targets := licenseGraph.Targets() sort.Sort(targets) fmt.Fprintf(stdout, "strict digraph {\n\trankdir=RL;\n") for _, target := range targets { makeNode(target) } } // Print the sorted edges to stdout ... for _, e := range edges { // sort the annotations for repeatability/stability annotations := e.Annotations().AsList() sort.Strings(annotations) tName := e.Target().Name() dName := e.Dependency().Name() if ctx.graphViz { // ... one edge per line labelled with \\n-separated annotations. tNode := nodes[tName] dNode := nodes[dName] fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", dNode, tNode, strings.Join(annotations, "\\n")) } else { // ... one edge per line with annotations in a colon-separated tuple. fmt.Fprintf(stdout, "%s %s %s\n", targetOut(e.Target(), ":"), targetOut(e.Dependency(), ":"), strings.Join(annotations, ":")) } } // If graphViz output, rank the root nodes together, and complete the directed graph. if ctx.graphViz { fmt.Fprintf(stdout, "\t{rank=same;") for _, f := range files { fName := f if !strings.HasSuffix(fName, ".meta_lic") { fName += ".meta_lic" } if fNode, ok := nodes[fName]; ok { fmt.Fprintf(stdout, " %s", fNode) } } fmt.Fprintf(stdout, "}\n}\n") } return nil } ================================================ FILE: tools/compliance/cmd/dumpgraph/dumpgraph_test.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "fmt" "os" "strings" "testing" "android/soong/tools/compliance" ) func TestMain(m *testing.M) { // Change into the parent directory before running the tests // so they can find the testdata directory. if err := os.Chdir(".."); err != nil { fmt.Printf("failed to change to testdata directory: %s\n", err) os.Exit(1) } os.Exit(m.Run()) } func Test_plaintext(t *testing.T) { tests := []struct { condition string name string outDir string roots []string ctx context expectedOut []string }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic static", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic static", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic static", }, }, { condition: "firstparty", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic static", "bin/bin1.meta_lic lib/libc.a.meta_lic static", "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", "highest.apex.meta_lic bin/bin1.meta_lic static", "highest.apex.meta_lic bin/bin2.meta_lic static", "highest.apex.meta_lic lib/liba.so.meta_lic static", "highest.apex.meta_lic lib/libb.so.meta_lic static", }, }, { condition: "firstparty", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static", "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", }, }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/lib/libd.so.meta_lic dynamic", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic static", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic static", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic static", }, }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/firstparty/application.meta_lic testdata/firstparty/bin/bin3.meta_lic toolchain", "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/application.meta_lic testdata/firstparty/lib/libb.so.meta_lic dynamic", }, }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic static", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic static", }, }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{}, }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic", "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic static", "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic static", "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic static", }, }, { condition: "notice", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}}, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic static", "bin/bin1.meta_lic lib/libc.a.meta_lic static", "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", "highest.apex.meta_lic bin/bin1.meta_lic static", "highest.apex.meta_lic bin/bin2.meta_lic static", "highest.apex.meta_lic lib/liba.so.meta_lic static", "highest.apex.meta_lic lib/libb.so.meta_lic static", }, }, { condition: "notice", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice static", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice static", "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice static", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", }, }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", "testdata/notice/bin/bin2.meta_lic testdata/notice/lib/libd.so.meta_lic dynamic", "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic static", "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic static", "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic static", }, }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/notice/application.meta_lic testdata/notice/bin/bin3.meta_lic toolchain", "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/application.meta_lic testdata/notice/lib/libb.so.meta_lic dynamic", }, }, { condition: "notice", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic static", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic static", }, }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{}, }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic static", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic static", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static", }, }, { condition: "reciprocal", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic static", "bin/bin1.meta_lic lib/libc.a.meta_lic static", "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", "highest.apex.meta_lic bin/bin1.meta_lic static", "highest.apex.meta_lic bin/bin2.meta_lic static", "highest.apex.meta_lic lib/liba.so.meta_lic static", "highest.apex.meta_lic lib/libb.so.meta_lic static", }, }, { condition: "reciprocal", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal static", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static", "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:notice dynamic", "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal static", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice static", }, }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/lib/libd.so.meta_lic dynamic", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic static", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic static", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic static", }, }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/application.meta_lic testdata/reciprocal/bin/bin3.meta_lic toolchain", "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/libb.so.meta_lic dynamic", }, }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic static", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic static", }, }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{}, }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic", "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic static", "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic static", "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic static", }, }, { condition: "restricted", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}}, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic static", "bin/bin1.meta_lic lib/libc.a.meta_lic static", "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", "highest.apex.meta_lic bin/bin1.meta_lic static", "highest.apex.meta_lic bin/bin2.meta_lic static", "highest.apex.meta_lic lib/liba.so.meta_lic static", "highest.apex.meta_lic lib/libb.so.meta_lic static", }, }, { condition: "restricted", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked static", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static", "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic", "bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked static", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static", }, }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic dynamic", "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic static", "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic static", "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic static", }, }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/restricted/application.meta_lic testdata/restricted/bin/bin3.meta_lic toolchain", "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic dynamic", }, }, { condition: "restricted", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic static", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic static", }, }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{}, }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic static", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic static", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic static", }, }, { condition: "proprietary", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic static", "bin/bin1.meta_lic lib/libc.a.meta_lic static", "bin/bin2.meta_lic lib/libb.so.meta_lic dynamic", "bin/bin2.meta_lic lib/libd.so.meta_lic dynamic", "highest.apex.meta_lic bin/bin1.meta_lic static", "highest.apex.meta_lic bin/bin2.meta_lic static", "highest.apex.meta_lic lib/liba.so.meta_lic static", "highest.apex.meta_lic lib/libb.so.meta_lic static", }, }, { condition: "proprietary", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary static", "bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted dynamic", "bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice dynamic", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static", "highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary static", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary static", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static", }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic dynamic", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic static", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic static", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic static", }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/proprietary/application.meta_lic testdata/proprietary/bin/bin3.meta_lic toolchain", "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic dynamic", }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic static", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic static", }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{}, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { expectedOut := &bytes.Buffer{} for _, eo := range tt.expectedOut { expectedOut.WriteString(eo) expectedOut.WriteString("\n") } stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } err := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) if err != nil { t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr) return } if stderr.Len() > 0 { t.Errorf("dumpgraph: gotStderr = %v, want none", stderr) } out := stdout.String() expected := expectedOut.String() if out != expected { outList := strings.Split(out, "\n") expectedList := strings.Split(expected, "\n") startLine := 0 for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] { startLine++ } t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v", out, expected, startLine+1, outList[startLine], expectedList[startLine]) } }) } } type testContext struct { nextNode int nodes map[string]string } type matcher interface { matchString(*testContext) string typeString() string } type targetMatcher struct { target string conditions []string } func (tm *targetMatcher) matchString(ctx *testContext) string { m := tm.target if len(tm.conditions) > 0 { m += "\\n" + strings.Join(tm.conditions, "\\n") } m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];" return m } func (tm *targetMatcher) typeString() string { return "target" } type edgeMatcher struct { target string dep string annotations []string } func (em *edgeMatcher) matchString(ctx *testContext) string { return ctx.nodes[em.dep] + " -> " + ctx.nodes[em.target] + " [label=\"" + strings.Join(em.annotations, "\\n") + "\"];" } func (tm *edgeMatcher) typeString() string { return "edge" } type getMatcher func(*testContext) matcher func matchTarget(target string, conditions ...string) getMatcher { return func(ctx *testContext) matcher { ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode) ctx.nextNode++ return &targetMatcher{target, append([]string{}, conditions...)} } } func matchEdge(target, dep string, annotations ...string) getMatcher { return func(ctx *testContext) matcher { if _, ok := ctx.nodes[target]; !ok { panic(fmt.Errorf("no node for target %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n"))) } if _, ok := ctx.nodes[dep]; !ok { panic(fmt.Errorf("no node for dep %v in %v -> %v [label=\"%s\"];", target, dep, target, strings.Join(annotations, "\\n"))) } return &edgeMatcher{target, dep, append([]string{}, annotations...)} } } func Test_graphviz(t *testing.T) { tests := []struct { condition string name string outDir string roots []string ctx context expectedOut []getMatcher }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/bin/bin2.meta_lic"), matchTarget("testdata/firstparty/highest.apex.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"), matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"), matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"), }, }, { condition: "firstparty", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("lib/libd.so.meta_lic"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "firstparty", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "notice"), matchTarget("lib/libc.a.meta_lic", "notice"), matchTarget("lib/libd.so.meta_lic", "notice"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/bin/bin2.meta_lic"), matchTarget("testdata/firstparty/container.zip.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "static"), matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "static"), matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "static"), }, }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/application.meta_lic"), matchTarget("testdata/firstparty/bin/bin3.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/bin/bin3.meta_lic", "toolchain"), matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "dynamic"), }, }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "static"), matchEdge("testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "static"), }, }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{matchTarget("testdata/firstparty/lib/libd.so.meta_lic")}, }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/bin/bin2.meta_lic"), matchTarget("testdata/notice/highest.apex.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libb.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchTarget("testdata/notice/lib/libd.so.meta_lic"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"), matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"), matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"), }, }, { condition: "notice", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("lib/libd.so.meta_lic"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "notice", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "notice"), matchTarget("lib/libc.a.meta_lic", "notice"), matchTarget("lib/libd.so.meta_lic", "notice"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/bin/bin2.meta_lic"), matchTarget("testdata/notice/container.zip.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libb.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchTarget("testdata/notice/lib/libd.so.meta_lic"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/notice/bin/bin2.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin1.meta_lic", "static"), matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin2.meta_lic", "static"), matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/container.zip.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "static"), }, }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/application.meta_lic"), matchTarget("testdata/notice/bin/bin3.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libb.so.meta_lic"), matchEdge("testdata/notice/application.meta_lic", "testdata/notice/bin/bin3.meta_lic", "toolchain"), matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/application.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "dynamic"), }, }, { condition: "notice", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "static"), matchEdge("testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "static"), }, }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{matchTarget("testdata/notice/lib/libd.so.meta_lic")}, }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), matchTarget("testdata/reciprocal/highest.apex.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"), matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"), matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"), }, }, { condition: "reciprocal", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("lib/libd.so.meta_lic"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "reciprocal", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "reciprocal"), matchTarget("lib/libb.so.meta_lic", "notice"), matchTarget("lib/libc.a.meta_lic", "reciprocal"), matchTarget("lib/libd.so.meta_lic", "notice"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), matchTarget("testdata/reciprocal/container.zip.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "static"), matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "static"), matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "static"), }, }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/application.meta_lic"), matchTarget("testdata/reciprocal/bin/bin3.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/bin/bin3.meta_lic", "toolchain"), matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "dynamic"), }, }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "static"), matchEdge("testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "static"), }, }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{matchTarget("testdata/reciprocal/lib/libd.so.meta_lic")}, }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/bin/bin2.meta_lic"), matchTarget("testdata/restricted/highest.apex.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libb.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchTarget("testdata/restricted/lib/libd.so.meta_lic"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"), matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"), matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"), }, }, { condition: "restricted", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("lib/libd.so.meta_lic"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "restricted", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchTarget("lib/libb.so.meta_lic", "restricted"), matchTarget("lib/libc.a.meta_lic", "reciprocal"), matchTarget("lib/libd.so.meta_lic", "notice"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/bin/bin2.meta_lic"), matchTarget("testdata/restricted/container.zip.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libb.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchTarget("testdata/restricted/lib/libd.so.meta_lic"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "static"), matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "static"), matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "static"), }, }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/application.meta_lic"), matchTarget("testdata/restricted/bin/bin3.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libb.so.meta_lic"), matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/bin/bin3.meta_lic", "toolchain"), matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/application.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "dynamic"), }, }, { condition: "restricted", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "static"), matchEdge("testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "static"), }, }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{matchTarget("testdata/restricted/lib/libd.so.meta_lic")}, }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/bin/bin2.meta_lic"), matchTarget("testdata/proprietary/highest.apex.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"), matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"), matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"), }, }, { condition: "proprietary", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("lib/libd.so.meta_lic"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "proprietary", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchTarget("lib/libb.so.meta_lic", "restricted"), matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchTarget("lib/libd.so.meta_lic", "notice"), matchEdge("bin/bin1.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("bin/bin1.meta_lic", "lib/libc.a.meta_lic", "static"), matchEdge("bin/bin2.meta_lic", "lib/libb.so.meta_lic", "dynamic"), matchEdge("bin/bin2.meta_lic", "lib/libd.so.meta_lic", "dynamic"), matchEdge("highest.apex.meta_lic", "bin/bin1.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "bin/bin2.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/liba.so.meta_lic", "static"), matchEdge("highest.apex.meta_lic", "lib/libb.so.meta_lic", "static"), }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/bin/bin2.meta_lic"), matchTarget("testdata/proprietary/container.zip.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), matchEdge("testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "dynamic"), matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "static"), matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "static"), matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "static"), }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/application.meta_lic"), matchTarget("testdata/proprietary/bin/bin3.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/bin/bin3.meta_lic", "toolchain"), matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "dynamic"), }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "static"), matchEdge("testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "static"), }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{matchTarget("testdata/proprietary/lib/libd.so.meta_lic")}, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { ctx := &testContext{0, make(map[string]string)} expectedOut := &bytes.Buffer{} for _, eo := range tt.expectedOut { m := eo(ctx) expectedOut.WriteString(m.matchString(ctx)) expectedOut.WriteString("\n") } stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } tt.ctx.graphViz = true err := dumpGraph(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) if err != nil { t.Fatalf("dumpgraph: error = %v, stderr = %v", err, stderr) return } if stderr.Len() > 0 { t.Errorf("dumpgraph: gotStderr = %v, want none", stderr) } outList := strings.Split(stdout.String(), "\n") outLine := 0 if outList[outLine] != "strict digraph {" { t.Errorf("dumpgraph: got 1st line %v, want strict digraph {", outList[outLine]) } outLine++ if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") { outLine++ } endOut := len(outList) for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" { endOut-- } if outList[endOut-1] != "}" { t.Errorf("dumpgraph: got last line %v, want }", outList[endOut-1]) } endOut-- if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") { endOut-- } expectedList := strings.Split(expectedOut.String(), "\n") for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" { expectedList = expectedList[0 : len(expectedList)-1] } matchLine := 0 for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] { outLine++ matchLine++ } if outLine < endOut || matchLine < len(expectedList) { if outLine >= endOut { t.Errorf("dumpgraph: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n")) } else if matchLine >= len(expectedList) { t.Errorf("dumpgraph: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n")) } else { t.Errorf("dumpgraph: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n")) } } }) } } ================================================ FILE: tools/compliance/cmd/dumpresolutions/dumpresolutions.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "flag" "fmt" "io" "io/fs" "os" "path/filepath" "sort" "strings" "android/soong/response" "android/soong/tools/compliance" ) var ( failNoneRequested = fmt.Errorf("\nNo license metadata files requested") failNoLicenses = fmt.Errorf("No licenses found") ) type context struct { conditions []compliance.LicenseCondition graphViz bool labelConditions bool stripPrefix []string } func (ctx context) strip(installPath string) string { for _, prefix := range ctx.stripPrefix { if strings.HasPrefix(installPath, prefix) { p := strings.TrimPrefix(installPath, prefix) if 0 == len(p) { continue } return p } } return installPath } // newMultiString creates a flag that allows multiple values in an array. func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { var f multiString flags.Var(&f, name, usage) return &f } // multiString implements the flag `Value` interface for multiple strings. type multiString []string func (ms *multiString) String() string { return strings.Join(*ms, ", ") } func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } func main() { var expandedArgs []string for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "@") { f, err := os.Open(strings.TrimPrefix(arg, "@")) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } respArgs, err := response.ReadRspFile(f) f.Close() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } expandedArgs = append(expandedArgs, respArgs...) } else { expandedArgs = append(expandedArgs, arg) } } flags := flag.NewFlagSet("flags", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} Outputs a space-separated Target ActsOn Origin Condition tuple for each resolution in the graph. When -dot flag given, outputs nodes and edges in graphviz directed graph format. If one or more '-c condition' conditions are given, outputs the resolution for the union of the conditions. Otherwise, outputs the resolution for all conditions. In plain text mode, when '-label_conditions' is requested, the Target and Origin have colon-separated license conditions appended: i.e. target:condition1:condition2 etc. Options: `, filepath.Base(os.Args[0])) flags.PrintDefaults() } conditions := newMultiString(flags, "c", "License condition to resolve. (may be given multiple times)") graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.") labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.") outputFile := flags.String("o", "-", "Where to write the output. (default stdout)") stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") flags.Parse(expandedArgs) // Must specify at least one root target. if flags.NArg() == 0 { flags.Usage() os.Exit(2) } if len(*outputFile) == 0 { flags.Usage() fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") os.Exit(2) } else { dir, err := filepath.Abs(filepath.Dir(*outputFile)) if err != nil { fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) os.Exit(1) } fi, err := os.Stat(dir) if err != nil { fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) os.Exit(1) } if !fi.IsDir() { fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) os.Exit(1) } } var ofile io.Writer ofile = os.Stdout var obuf *bytes.Buffer if *outputFile != "-" { obuf = &bytes.Buffer{} ofile = obuf } lcs := make([]compliance.LicenseCondition, 0, len(*conditions)) for _, name := range *conditions { lcs = append(lcs, compliance.RecognizedConditionNames[name]) } ctx := &context{ conditions: lcs, graphViz: *graphViz, labelConditions: *labelConditions, stripPrefix: *stripPrefix, } _, err := dumpResolutions(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...) if err != nil { if err == failNoneRequested { flags.Usage() } fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) } if *outputFile != "-" { err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) if err != nil { fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err) os.Exit(1) } } os.Exit(0) } // dumpResolutions implements the dumpresolutions utility. func dumpResolutions(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) { if len(files) < 1 { return nil, failNoneRequested } // Read the license graph from the license metadata files (*.meta_lic). licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files) if err != nil { return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) } if licenseGraph == nil { return nil, failNoLicenses } compliance.ResolveTopDownConditions(licenseGraph) cs := compliance.AllLicenseConditions if len(ctx.conditions) > 0 { cs = compliance.NewLicenseConditionSet() for _, c := range ctx.conditions { cs = cs.Plus(c) } } resolutions := compliance.WalkResolutionsForCondition(licenseGraph, cs) // nodes maps license metadata file names to graphViz node names when graphViz requested. nodes := make(map[string]string) n := 0 // targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed. targetOut := func(target *compliance.TargetNode, sep string) string { tOut := ctx.strip(target.Name()) if ctx.labelConditions { conditions := target.LicenseConditions().Names() if len(conditions) > 0 { tOut += sep + strings.Join(conditions, sep) } } return tOut } // makeNode maps `target` to a graphViz node name. makeNode := func(target *compliance.TargetNode) { tName := target.Name() if _, ok := nodes[tName]; !ok { nodeName := fmt.Sprintf("n%d", n) nodes[tName] = nodeName fmt.Fprintf(stdout, "\t%s [label=\"%s\"];\n", nodeName, targetOut(target, "\\n")) n++ } } // outputResolution prints a resolution in the requested format to `stdout`, where one can read // a resolution as `tname` resolves `oname`'s conditions named in `cnames`. // `tname` is the name of the target the resolution applies to. // `cnames` is the list of conditions to resolve. outputResolution := func(tname, aname string, cnames []string) { if ctx.graphViz { // ... one edge per line labelled with \\n-separated annotations. tNode := nodes[tname] aNode := nodes[aname] fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", tNode, aNode, strings.Join(cnames, "\\n")) } else { // ... one edge per line with names in a colon-separated tuple. fmt.Fprintf(stdout, "%s %s %s\n", tname, aname, strings.Join(cnames, ":")) } } // Sort the resolutions by targetname for repeatability/stability. targets := resolutions.AttachesTo() sort.Sort(targets) // If graphviz output, start the directed graph. if ctx.graphViz { fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n") for _, target := range targets { makeNode(target) rl := resolutions.Resolutions(target) sort.Sort(rl) for _, r := range rl { makeNode(r.ActsOn()) } } } // Output the sorted targets. for _, target := range targets { var tname string if ctx.graphViz { tname = target.Name() } else { tname = targetOut(target, ":") } rl := resolutions.Resolutions(target) sort.Sort(rl) for _, r := range rl { var aname string if ctx.graphViz { aname = r.ActsOn().Name() } else { aname = targetOut(r.ActsOn(), ":") } // cnames accumulates the list of condition names originating at a single origin that apply to `target`. cnames := r.Resolves().Names() // Output 1 line for each attachesTo+actsOn combination. outputResolution(tname, aname, cnames) } } // If graphViz output, rank the root nodes together, and complete the directed graph. if ctx.graphViz { fmt.Fprintf(stdout, "\t{rank=same;") for _, f := range files { fName := f if !strings.HasSuffix(fName, ".meta_lic") { fName += ".meta_lic" } if fNode, ok := nodes[fName]; ok { fmt.Fprintf(stdout, " %s", fNode) } } fmt.Fprintf(stdout, "}\n}\n") } return licenseGraph, nil } ================================================ FILE: tools/compliance/cmd/dumpresolutions/dumpresolutions_test.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "fmt" "os" "strings" "testing" "android/soong/tools/compliance" ) func TestMain(m *testing.M) { // Change into the parent directory before running the tests // so they can find the testdata directory. if err := os.Chdir(".."); err != nil { fmt.Printf("failed to change to testdata directory: %s\n", err) os.Exit(1) } os.Exit(m.Run()) } func Test_plaintext(t *testing.T) { tests := []struct { condition string name string outDir string roots []string ctx context expectedOut []string }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", "testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", }, }, { condition: "firstparty", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic notice", "bin/bin1.meta_lic lib/libc.a.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/liba.so.meta_lic notice", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "highest.apex.meta_lic lib/libc.a.meta_lic notice", "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "firstparty", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic notice", "bin/bin1.meta_lic lib/libc.a.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/liba.so.meta_lic notice", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "highest.apex.meta_lic lib/libc.a.meta_lic notice", "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "firstparty", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []string{}, }, { condition: "firstparty", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []string{}, }, { condition: "firstparty", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: append(compliance.ImpliesPrivate.AsList(), compliance.ImpliesShared.AsList()...), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []string{}, }, { condition: "firstparty", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice", "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice", "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice", "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", }, }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", "testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", "testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", "testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice", }, }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice", "testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", }, }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice", "testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice", }, }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{ "testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice", }, }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic notice", "testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic notice", "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice", }, }, { condition: "notice", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}}, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic notice", "bin/bin1.meta_lic lib/libc.a.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/liba.so.meta_lic notice", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "highest.apex.meta_lic lib/libc.a.meta_lic notice", "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "notice", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic notice", "bin/bin1.meta_lic lib/libc.a.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/liba.so.meta_lic notice", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "highest.apex.meta_lic lib/libc.a.meta_lic notice", "lib/liba.so.meta_lic lib/liba.so.meta_lic notice", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "notice", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []string{}, }, { condition: "notice", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []string{}, }, { condition: "notice", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []string{}, }, { condition: "notice", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice", "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice", "lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice", "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", }, }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", "testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic notice", "testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic notice", "testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice", }, }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice", "testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic notice", }, }, { condition: "notice", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice", "testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice", }, }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{ "testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice", }, }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", "testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", }, }, { condition: "reciprocal", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "reciprocal", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", "highest.apex.meta_lic lib/libb.so.meta_lic notice", "lib/libb.so.meta_lic lib/libb.so.meta_lic notice", }, }, { condition: "reciprocal", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", }, }, { condition: "reciprocal", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []string{}, }, { condition: "reciprocal", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal", "highest.apex.meta_lic lib/liba.so.meta_lic reciprocal", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal", "lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal", }, }, { condition: "reciprocal", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal", "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice", "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice", "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal", "lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal", "lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice", }, }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", "testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", "testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", "testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice", }, }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice", "testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", }, }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal", "testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal", }, }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{ "testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice", }, }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked", "testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", "testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice:restricted:restricted_if_statically_linked", "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", "testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", }, }, { condition: "restricted", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}}, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice:restricted_if_statically_linked", "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "bin/bin2.meta_lic bin/bin2.meta_lic notice:restricted", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin1.meta_lic notice:restricted_if_statically_linked", "highest.apex.meta_lic bin/bin2.meta_lic notice:restricted", "highest.apex.meta_lic highest.apex.meta_lic notice:restricted:restricted_if_statically_linked", "highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "restricted", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin2.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", }, }, { condition: "restricted", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic restricted_if_statically_linked", "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "bin/bin2.meta_lic bin/bin2.meta_lic restricted", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin1.meta_lic restricted_if_statically_linked", "highest.apex.meta_lic bin/bin2.meta_lic restricted", "highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_if_statically_linked", "highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "restricted", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []string{}, }, { condition: "restricted", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic restricted_if_statically_linked", "bin/bin1.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "bin/bin2.meta_lic bin/bin2.meta_lic restricted", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin1.meta_lic restricted_if_statically_linked", "highest.apex.meta_lic bin/bin2.meta_lic restricted", "highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_if_statically_linked", "highest.apex.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_if_statically_linked", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "restricted", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_if_statically_linked", "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_if_statically_linked", "bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted", "bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_if_statically_linked", "highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted", "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted:restricted_if_statically_linked", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", "highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_if_statically_linked", "lib/liba.so.meta_lic:restricted_if_statically_linked lib/liba.so.meta_lic:restricted_if_statically_linked restricted_if_statically_linked", "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted", }, }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", "testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked", "testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted", "testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice:restricted:restricted_if_statically_linked", "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", "testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", "testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted", }, }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice:restricted:restricted_if_statically_linked", "testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted:restricted_if_statically_linked", }, }, { condition: "restricted", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked", "testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_if_statically_linked", }, }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{ "testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice", }, }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice:restricted", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", "testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", }, }, { condition: "proprietary", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only", "bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only", "highest.apex.meta_lic highest.apex.meta_lic notice:restricted", "highest.apex.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only", "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "proprietary", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []string{ "bin/bin1.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic bin/bin1.meta_lic notice", "highest.apex.meta_lic highest.apex.meta_lic notice", }, }, { condition: "proprietary", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []string{ "bin/bin2.meta_lic bin/bin2.meta_lic restricted", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin2.meta_lic restricted", "highest.apex.meta_lic highest.apex.meta_lic restricted", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "proprietary", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary", "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary", "bin/bin2.meta_lic bin/bin2.meta_lic proprietary", "highest.apex.meta_lic bin/bin2.meta_lic proprietary", "highest.apex.meta_lic lib/liba.so.meta_lic proprietary", "highest.apex.meta_lic lib/libc.a.meta_lic proprietary", "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary", }, }, { condition: "proprietary", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: append(compliance.ImpliesShared.AsList(), compliance.ImpliesPrivate.AsList()...), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []string{ "bin/bin1.meta_lic lib/liba.so.meta_lic proprietary", "bin/bin1.meta_lic lib/libc.a.meta_lic proprietary", "bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary", "bin/bin2.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary", "highest.apex.meta_lic highest.apex.meta_lic restricted", "highest.apex.meta_lic lib/liba.so.meta_lic proprietary", "highest.apex.meta_lic lib/libb.so.meta_lic restricted", "highest.apex.meta_lic lib/libc.a.meta_lic proprietary", "lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary", "lib/libb.so.meta_lic lib/libb.so.meta_lic restricted", }, }, { condition: "proprietary", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, expectedOut: []string{ "bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice", "bin/bin1.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", "bin/bin1.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", "bin/bin2.meta_lic:proprietary:by_exception_only bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only", "bin/bin2.meta_lic:proprietary:by_exception_only lib/libb.so.meta_lic:restricted restricted", "highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice", "highest.apex.meta_lic:notice bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only", "highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted", "highest.apex.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", "highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted", "highest.apex.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", "lib/liba.so.meta_lic:proprietary:by_exception_only lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only", "lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted", }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", "testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice:restricted", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", "testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", "testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted", }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []string{ "testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice:restricted", "testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic restricted:proprietary:by_exception_only", }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []string{ "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only", "testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only", }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []string{ "testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice", }, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { expectedOut := &bytes.Buffer{} for _, eo := range tt.expectedOut { expectedOut.WriteString(eo) expectedOut.WriteString("\n") } stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } _, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) if err != nil { t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr) return } if stderr.Len() > 0 { t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr) } out := stdout.String() expected := expectedOut.String() if out != expected { outList := strings.Split(out, "\n") expectedList := strings.Split(expected, "\n") startLine := 0 for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] { startLine++ } t.Errorf("dumpresoliutions: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v", out, expected, startLine+1, outList[startLine], expectedList[startLine]) } }) } } type testContext struct { nextNode int nodes map[string]string } type matcher interface { matchString(*testContext, *compliance.LicenseGraph) string typeString() string } type targetMatcher struct { target string conditions []string } // newTestCondition constructs a test license condition in the license graph. func newTestCondition(lg *compliance.LicenseGraph, conditionName ...string) compliance.LicenseConditionSet { cs := compliance.NewLicenseConditionSet() for _, name := range conditionName { cs = cs.Plus(compliance.RecognizedConditionNames[name]) } if cs.IsEmpty() && len(conditionName) != 0 { panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName)) } return cs } func (tm *targetMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string { cs := newTestCondition(lg, tm.conditions...) m := tm.target if !cs.IsEmpty() { m += "\\n" + strings.Join(cs.Names(), "\\n") } m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];" return m } func (tm *targetMatcher) typeString() string { return "target" } type resolutionMatcher struct { appliesTo string actsOn string conditions []string } func (rm *resolutionMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string { cs := newTestCondition(lg, rm.conditions...) return ctx.nodes[rm.appliesTo] + " -> " + ctx.nodes[rm.actsOn] + " [label=\"" + strings.Join(cs.Names(), "\\n") + "\"];" } func (rm *resolutionMatcher) typeString() string { return "resolution" } type getMatcher func(*testContext) matcher func matchTarget(target string, conditions ...string) getMatcher { return func(ctx *testContext) matcher { ctx.nodes[target] = fmt.Sprintf("n%d", ctx.nextNode) ctx.nextNode++ return &targetMatcher{target, append([]string{}, conditions...)} } } func matchResolution(appliesTo, actsOn string, conditions ...string) getMatcher { return func(ctx *testContext) matcher { if _, ok := ctx.nodes[appliesTo]; !ok { ctx.nodes[appliesTo] = fmt.Sprintf("unknown%d", ctx.nextNode) ctx.nextNode++ } if _, ok := ctx.nodes[actsOn]; !ok { ctx.nodes[actsOn] = fmt.Sprintf("unknown%d", ctx.nextNode) ctx.nextNode++ } return &resolutionMatcher{appliesTo, actsOn, append([]string{}, conditions...)} } } func Test_graphviz(t *testing.T) { tests := []struct { condition string name string outDir string roots []string ctx context expectedOut []getMatcher }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchTarget("testdata/firstparty/bin/bin2.meta_lic"), matchTarget("testdata/firstparty/highest.apex.meta_lic"), matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/highest.apex.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []getMatcher{}, }, { condition: "firstparty", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []getMatcher{}, }, { condition: "firstparty", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), stripPrefix: []string{"testdata/firstparty/"}, }, expectedOut: []getMatcher{}, }, { condition: "firstparty", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/firstparty/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "notice"), matchTarget("lib/libc.a.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchTarget("testdata/firstparty/bin/bin2.meta_lic"), matchTarget("testdata/firstparty/container.zip.meta_lic"), matchTarget("testdata/firstparty/lib/libb.so.meta_lic"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/container.zip.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/application.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchResolution( "testdata/firstparty/application.meta_lic", "testdata/firstparty/application.meta_lic", "notice"), matchResolution( "testdata/firstparty/application.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), }, }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/bin/bin1.meta_lic"), matchTarget("testdata/firstparty/lib/liba.so.meta_lic"), matchTarget("testdata/firstparty/lib/libc.a.meta_lic"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "notice"), }, }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/firstparty/lib/libd.so.meta_lic"), matchResolution( "testdata/firstparty/lib/libd.so.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", "notice"), }, }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchTarget("testdata/notice/bin/bin2.meta_lic"), matchTarget("testdata/notice/highest.apex.meta_lic"), matchTarget("testdata/notice/lib/libb.so.meta_lic"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin2.meta_lic", "testdata/notice/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/highest.apex.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/lib/libb.so.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "notice"), }, }, { condition: "notice", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "notice", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "notice", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []getMatcher{}, }, { condition: "notice", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []getMatcher{}, }, { condition: "notice", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), stripPrefix: []string{"testdata/notice/"}, }, expectedOut: []getMatcher{}, }, { condition: "notice", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/notice/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "notice"), matchTarget("lib/libc.a.meta_lic", "notice"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "notice"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchTarget("testdata/notice/bin/bin2.meta_lic"), matchTarget("testdata/notice/container.zip.meta_lic"), matchTarget("testdata/notice/lib/libb.so.meta_lic"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin2.meta_lic", "testdata/notice/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/container.zip.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/notice/container.zip.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "notice"), matchResolution( "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/lib/libb.so.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "notice"), }, }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/application.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchResolution( "testdata/notice/application.meta_lic", "testdata/notice/application.meta_lic", "notice"), matchResolution( "testdata/notice/application.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), }, }, { condition: "notice", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/bin/bin1.meta_lic"), matchTarget("testdata/notice/lib/liba.so.meta_lic"), matchTarget("testdata/notice/lib/libc.a.meta_lic"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "notice"), matchResolution( "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "notice"), }, }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/notice/lib/libd.so.meta_lic"), matchResolution( "testdata/notice/lib/libd.so.meta_lic", "testdata/notice/lib/libd.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), matchTarget("testdata/reciprocal/highest.apex.meta_lic"), matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/highest.apex.meta_lic", "notice"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/lib/libb.so.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), }, }, { condition: "reciprocal", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []getMatcher{}, }, { condition: "reciprocal", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), stripPrefix: []string{"testdata/reciprocal/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), }, }, { condition: "reciprocal", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/reciprocal/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "reciprocal"), matchTarget("lib/libc.a.meta_lic", "reciprocal"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("highest.apex.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "reciprocal"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchTarget("testdata/reciprocal/bin/bin2.meta_lic"), matchTarget("testdata/reciprocal/container.zip.meta_lic"), matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "notice"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/container.zip.meta_lic", "notice"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "notice"), matchResolution( "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/lib/libb.so.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "notice"), }, }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/application.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchResolution( "testdata/reciprocal/application.meta_lic", "testdata/reciprocal/application.meta_lic", "notice"), matchResolution( "testdata/reciprocal/application.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), }, }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/bin/bin1.meta_lic"), matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"), matchTarget("testdata/reciprocal/lib/libc.a.meta_lic"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "reciprocal"), matchResolution( "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "reciprocal"), }, }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"), matchResolution( "testdata/reciprocal/lib/libd.so.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", "notice"), }, }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchTarget("testdata/restricted/bin/bin2.meta_lic"), matchTarget("testdata/restricted/lib/libb.so.meta_lic"), matchTarget("testdata/restricted/highest.apex.meta_lic"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/highest.apex.meta_lic", "restricted", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/lib/libb.so.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "restricted_if_statically_linked", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), }, }, { condition: "restricted", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []getMatcher{}, }, { condition: "restricted", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), stripPrefix: []string{"testdata/restricted/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/restricted/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchTarget("lib/libc.a.meta_lic", "reciprocal"), matchTarget("bin/bin2.meta_lic", "notice"), matchTarget("lib/libb.so.meta_lic", "restricted"), matchTarget("highest.apex.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "restricted_if_statically_linked", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchTarget("testdata/restricted/bin/bin2.meta_lic"), matchTarget("testdata/restricted/lib/libb.so.meta_lic"), matchTarget("testdata/restricted/container.zip.meta_lic"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "restricted", "notice"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/container.zip.meta_lic", "restricted", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "reciprocal", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/lib/libb.so.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "restricted"), }, }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/application.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchResolution( "testdata/restricted/application.meta_lic", "testdata/restricted/application.meta_lic", "restricted", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/application.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked", "restricted"), }, }, { condition: "restricted", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/bin/bin1.meta_lic"), matchTarget("testdata/restricted/lib/liba.so.meta_lic"), matchTarget("testdata/restricted/lib/libc.a.meta_lic"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/bin/bin1.meta_lic", "restricted_if_statically_linked", "notice"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "restricted_if_statically_linked"), matchResolution( "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "restricted_if_statically_linked", "reciprocal"), }, }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/restricted/lib/libd.so.meta_lic"), matchResolution( "testdata/restricted/lib/libd.so.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", "notice"), }, }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchTarget("testdata/proprietary/bin/bin2.meta_lic"), matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), matchTarget("testdata/proprietary/highest.apex.meta_lic"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/highest.apex.meta_lic", "restricted", "notice"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/lib/libb.so.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "apex_trimmed", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "by_exception_only", "restricted", "proprietary"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "apex_trimmed_notice", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: []compliance.LicenseCondition{compliance.NoticeCondition}, stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "notice"), }, }, { condition: "proprietary", name: "apex_trimmed_share", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.AsList(), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "apex_trimmed_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesPrivate.AsList(), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "proprietary"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "proprietary"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "proprietary"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "proprietary"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "proprietary"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "proprietary"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "proprietary"), }, }, { condition: "proprietary", name: "apex_trimmed_share_private", roots: []string{"highest.apex.meta_lic"}, ctx: context{ conditions: compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(), stripPrefix: []string{"testdata/proprietary/"}, }, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic"), matchTarget("lib/liba.so.meta_lic"), matchTarget("lib/libc.a.meta_lic"), matchTarget("bin/bin2.meta_lic"), matchTarget("lib/libb.so.meta_lic"), matchTarget("highest.apex.meta_lic"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "proprietary"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "proprietary"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted", "proprietary"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted", "proprietary"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "proprietary"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "proprietary"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "proprietary"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "apex_trimmed_labelled", roots: []string{"highest.apex.meta_lic"}, ctx: context{stripPrefix: []string{"testdata/proprietary/"}, labelConditions: true}, expectedOut: []getMatcher{ matchTarget("bin/bin1.meta_lic", "notice"), matchTarget("lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"), matchTarget("lib/libb.so.meta_lic", "restricted"), matchTarget("highest.apex.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "bin/bin1.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "bin/bin1.meta_lic", "lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "bin/bin2.meta_lic", "bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "bin/bin2.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "bin/bin1.meta_lic", "notice"), matchResolution( "highest.apex.meta_lic", "bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "highest.apex.meta_lic", "highest.apex.meta_lic", "restricted", "notice"), matchResolution( "highest.apex.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "highest.apex.meta_lic", "lib/libb.so.meta_lic", "restricted"), matchResolution( "highest.apex.meta_lic", "lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "lib/liba.so.meta_lic", "lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "lib/libb.so.meta_lic", "lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchTarget("testdata/proprietary/bin/bin2.meta_lic"), matchTarget("testdata/proprietary/lib/libb.so.meta_lic"), matchTarget("testdata/proprietary/container.zip.meta_lic"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "restricted", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/container.zip.meta_lic", "restricted", "notice"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), matchResolution( "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/lib/libb.so.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "restricted"), }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/application.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchResolution( "testdata/proprietary/application.meta_lic", "testdata/proprietary/application.meta_lic", "notice", "restricted"), matchResolution( "testdata/proprietary/application.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "restricted", "by_exception_only", "proprietary"), }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/bin/bin1.meta_lic"), matchTarget("testdata/proprietary/lib/liba.so.meta_lic"), matchTarget("testdata/proprietary/lib/libc.a.meta_lic"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/bin/bin1.meta_lic", "notice"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "by_exception_only", "proprietary"), matchResolution( "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "by_exception_only", "proprietary"), }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []getMatcher{ matchTarget("testdata/proprietary/lib/libd.so.meta_lic"), matchResolution( "testdata/proprietary/lib/libd.so.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "notice"), }, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { ctx := &testContext{0, make(map[string]string)} stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } tt.ctx.graphViz = true lg, err := dumpResolutions(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...) if err != nil { t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr) return } if stderr.Len() > 0 { t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr) } expectedOut := &bytes.Buffer{} for _, eo := range tt.expectedOut { m := eo(ctx) expectedOut.WriteString(m.matchString(ctx, lg)) expectedOut.WriteString("\n") } outList := strings.Split(stdout.String(), "\n") outLine := 0 if outList[outLine] != "strict digraph {" { t.Errorf("dumpresolutions: got 1st line %v, want strict digraph {", outList[outLine]) } outLine++ if strings.HasPrefix(strings.TrimLeft(outList[outLine], " \t"), "rankdir") { outLine++ } endOut := len(outList) for endOut > 0 && strings.TrimLeft(outList[endOut-1], " \t") == "" { endOut-- } if outList[endOut-1] != "}" { t.Errorf("dumpresolutions: got last line %v, want }", outList[endOut-1]) } endOut-- if strings.HasPrefix(strings.TrimLeft(outList[endOut-1], " \t"), "{rank=same") { endOut-- } expectedList := strings.Split(expectedOut.String(), "\n") for len(expectedList) > 0 && expectedList[len(expectedList)-1] == "" { expectedList = expectedList[0 : len(expectedList)-1] } matchLine := 0 for outLine < endOut && matchLine < len(expectedList) && strings.TrimLeft(outList[outLine], " \t") == expectedList[matchLine] { outLine++ matchLine++ } if outLine < endOut || matchLine < len(expectedList) { if outLine >= endOut { t.Errorf("dumpresolutions: missing lines at end of graph, want %d lines %v", len(expectedList)-matchLine, strings.Join(expectedList[matchLine:], "\n")) } else if matchLine >= len(expectedList) { t.Errorf("dumpresolutions: unexpected lines at end of graph starting line %d, got %v, want nothing", outLine+1, strings.Join(outList[outLine:], "\n")) } else { t.Errorf("dumpresolutions: at line %d, got %v, want %v", outLine+1, strings.Join(outList[outLine:], "\n"), strings.Join(expectedList[matchLine:], "\n")) } } }) } } ================================================ FILE: tools/compliance/cmd/htmlnotice/htmlnotice.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "compress/gzip" "flag" "fmt" "html" "io" "io/fs" "os" "path/filepath" "sort" "strings" "android/soong/response" "android/soong/tools/compliance" "github.com/google/blueprint/deptools" ) var ( failNoneRequested = fmt.Errorf("\nNo license metadata files requested") failNoLicenses = fmt.Errorf("No licenses found") ) type context struct { stdout io.Writer stderr io.Writer rootFS fs.FS includeTOC bool product string stripPrefix []string title string deps *[]string } func (ctx context) strip(installPath string) string { for _, prefix := range ctx.stripPrefix { if strings.HasPrefix(installPath, prefix) { p := strings.TrimPrefix(installPath, prefix) if 0 == len(p) { p = ctx.product } if 0 == len(p) { continue } return p } } return installPath } // newMultiString creates a flag that allows multiple values in an array. func newMultiString(flags *flag.FlagSet, name, usage string) *multiString { var f multiString flags.Var(&f, name, usage) return &f } // multiString implements the flag `Value` interface for multiple strings. type multiString []string func (ms *multiString) String() string { return strings.Join(*ms, ", ") } func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil } func main() { var expandedArgs []string for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "@") { f, err := os.Open(strings.TrimPrefix(arg, "@")) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } respArgs, err := response.ReadRspFile(f) f.Close() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } expandedArgs = append(expandedArgs, respArgs...) } else { expandedArgs = append(expandedArgs, arg) } } flags := flag.NewFlagSet("flags", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...} Outputs an html NOTICE.html or gzipped NOTICE.html.gz file if the -o filename ends with ".gz". Options: `, filepath.Base(os.Args[0])) flags.PrintDefaults() } outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)") depsFile := flags.String("d", "", "Where to write the deps file") includeTOC := flags.Bool("toc", true, "Whether to include a table of contents.") product := flags.String("product", "", "The name of the product for which the notice is generated.") stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)") title := flags.String("title", "", "The title of the notice file.") flags.Parse(expandedArgs) // Must specify at least one root target. if flags.NArg() == 0 { flags.Usage() os.Exit(2) } if len(*outputFile) == 0 { flags.Usage() fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n") os.Exit(2) } else { dir, err := filepath.Abs(filepath.Dir(*outputFile)) if err != nil { fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err) os.Exit(1) } fi, err := os.Stat(dir) if err != nil { fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err) os.Exit(1) } if !fi.IsDir() { fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile) os.Exit(1) } } var ofile io.Writer var closer io.Closer ofile = os.Stdout var obuf *bytes.Buffer if *outputFile != "-" { obuf = &bytes.Buffer{} ofile = obuf } if strings.HasSuffix(*outputFile, ".gz") { ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression) closer = ofile.(io.Closer) } var deps []string ctx := &context{ofile, os.Stderr, compliance.FS, *includeTOC, *product, *stripPrefix, *title, &deps} err := htmlNotice(ctx, flags.Args()...) if err != nil { if err == failNoneRequested { flags.Usage() } fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) } if closer != nil { closer.Close() } if *outputFile != "-" { err := os.WriteFile(*outputFile, obuf.Bytes(), 0666) if err != nil { fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err) os.Exit(1) } } if *depsFile != "" { err := deptools.WriteDepFile(*depsFile, *outputFile, deps) if err != nil { fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err) os.Exit(1) } } os.Exit(0) } // htmlNotice implements the htmlnotice utility. func htmlNotice(ctx *context, files ...string) error { // Must be at least one root file. if len(files) < 1 { return failNoneRequested } // Read the license graph from the license metadata files (*.meta_lic). licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files) if err != nil { return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err) } if licenseGraph == nil { return failNoLicenses } // rs contains all notice resolutions. rs := compliance.ResolveNotices(licenseGraph) ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs) if err != nil { return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err) } fmt.Fprintln(ctx.stdout, "") fmt.Fprintln(ctx.stdout, "") fmt.Fprintln(ctx.stdout, "") if len(ctx.title) > 0 { fmt.Fprintf(ctx.stdout, "%s\n", html.EscapeString(ctx.title)) } else if len(ctx.product) > 0 { fmt.Fprintf(ctx.stdout, "%s\n", html.EscapeString(ctx.product)) } fmt.Fprintln(ctx.stdout, "") fmt.Fprintln(ctx.stdout, "") if len(ctx.title) > 0 { fmt.Fprintf(ctx.stdout, "

%s

\n", html.EscapeString(ctx.title)) } else if len(ctx.product) > 0 { fmt.Fprintf(ctx.stdout, "

%s

\n", html.EscapeString(ctx.product)) } ids := make(map[string]string) if ctx.includeTOC { fmt.Fprintln(ctx.stdout, "
    ") i := 0 for installPath := range ni.InstallPaths() { id := fmt.Sprintf("id%d", i) i++ ids[installPath] = id fmt.Fprintf(ctx.stdout, "
  • %s\n
      \n", id, html.EscapeString(ctx.strip(installPath))) for _, h := range ni.InstallHashes(installPath) { libs := ni.InstallHashLibs(installPath, h) fmt.Fprintf(ctx.stdout, "
    • %s\n", h.String(), html.EscapeString(strings.Join(libs, ", "))) } fmt.Fprintln(ctx.stdout, "
    ") } fmt.Fprintln(ctx.stdout, "
") } for h := range ni.Hashes() { fmt.Fprintln(ctx.stdout, "
") for _, libName := range ni.HashLibs(h) { fmt.Fprintf(ctx.stdout, " %s used by:\n
    \n", html.EscapeString(libName)) for _, installPath := range ni.HashLibInstalls(h, libName) { if id, ok := ids[installPath]; ok { fmt.Fprintf(ctx.stdout, "
  • %s\n", id, html.EscapeString(ctx.strip(installPath))) } else { fmt.Fprintf(ctx.stdout, "
  • %s\n", html.EscapeString(ctx.strip(installPath))) } } fmt.Fprintf(ctx.stdout, "
\n") } fmt.Fprintf(ctx.stdout, " \n
", h.String())
		fmt.Fprintln(ctx.stdout, html.EscapeString(string(ni.HashText(h))))
		fmt.Fprintln(ctx.stdout, "  
") } fmt.Fprintln(ctx.stdout, "") *ctx.deps = ni.InputFiles() sort.Strings(*ctx.deps) return nil } ================================================ FILE: tools/compliance/cmd/htmlnotice/htmlnotice_test.go ================================================ // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bufio" "bytes" "fmt" "html" "os" "reflect" "regexp" "strings" "testing" "android/soong/tools/compliance" ) var ( horizontalRule = regexp.MustCompile(`^\s*
\s*$`) bodyTag = regexp.MustCompile(`^\s*\s*$`) boilerPlate = regexp.MustCompile(`^\s*(?:
    |
      |\s*$`) libraryName = regexp.MustCompile(`^\s*(.*)\s\s*used\s\s*by\s*:\s*$`) licenseText = regexp.MustCompile(`^\s*
      (.*)$`)
      	titleTag       = regexp.MustCompile(`^\s*(.*)\s*$`)
      	h1Tag          = regexp.MustCompile(`^\s*

      (.*)

      \s*$`) usedByTarget = regexp.MustCompile(`^\s*
    • (?:)?((?:out/(?:[^/<]*/)+)[^/<]*)(?:)?\s*$`) installTarget = regexp.MustCompile(`^\s*
    • (.*)\s*$`) libReference = regexp.MustCompile(`^\s*
    • (.*)\s*$`) ) func TestMain(m *testing.M) { // Change into the parent directory before running the tests // so they can find the testdata directory. if err := os.Chdir(".."); err != nil { fmt.Printf("failed to change to testdata directory: %s\n", err) os.Exit(1) } os.Exit(m.Run()) } func Test(t *testing.T) { tests := []struct { condition string name string outDir string roots []string includeTOC bool stripPrefix string title string expectedOut []matcher expectedDeps []string }{ { condition: "firstparty", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/liba.so"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "firstparty", name: "apex+toc", roots: []string{"highest.apex.meta_lic"}, includeTOC: true, expectedOut: []matcher{ toc{}, target{"highest.apex"}, uses{"Android"}, target{"highest.apex/bin/bin1"}, uses{"Android"}, target{"highest.apex/bin/bin2"}, uses{"Android"}, target{"highest.apex/lib/liba.so"}, uses{"Android"}, target{"highest.apex/lib/libb.so"}, uses{"Android"}, hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/liba.so"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "firstparty", name: "apex-with-title", roots: []string{"highest.apex.meta_lic"}, title: "Emperor", expectedOut: []matcher{ pageTitle{"Emperor"}, hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/liba.so"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "firstparty", name: "apex-with-title+toc", roots: []string{"highest.apex.meta_lic"}, includeTOC: true, title: "Emperor", expectedOut: []matcher{ pageTitle{"Emperor"}, toc{}, target{"highest.apex"}, uses{"Android"}, target{"highest.apex/bin/bin1"}, uses{"Android"}, target{"highest.apex/bin/bin2"}, uses{"Android"}, target{"highest.apex/lib/liba.so"}, uses{"Android"}, target{"highest.apex/lib/libb.so"}, uses{"Android"}, hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/liba.so"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/highest.apex.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "firstparty", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"container.zip"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/bin2"}, usedBy{"container.zip/liba.so"}, usedBy{"container.zip/libb.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/bin/bin2.meta_lic", "testdata/firstparty/container.zip.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "firstparty", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"application"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/application.meta_lic", "testdata/firstparty/bin/bin3.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libb.so.meta_lic", }, }, { condition: "firstparty", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"bin/bin1"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/bin/bin1.meta_lic", "testdata/firstparty/lib/liba.so.meta_lic", "testdata/firstparty/lib/libc.a.meta_lic", }, }, { condition: "firstparty", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"lib/libd.so"}, firstParty{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/firstparty/lib/libd.so.meta_lic", }, }, { condition: "notice", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, hr{}, library{"Device"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/lib/liba.so"}, library{"External"}, usedBy{"highest.apex/bin/bin1"}, notice{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/notice/NOTICE_LICENSE", "testdata/notice/bin/bin1.meta_lic", "testdata/notice/bin/bin2.meta_lic", "testdata/notice/highest.apex.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "testdata/notice/lib/libd.so.meta_lic", }, }, { condition: "notice", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"container.zip"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/bin2"}, usedBy{"container.zip/libb.so"}, firstParty{}, hr{}, library{"Device"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/liba.so"}, library{"External"}, usedBy{"container.zip/bin1"}, notice{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/notice/NOTICE_LICENSE", "testdata/notice/bin/bin1.meta_lic", "testdata/notice/bin/bin2.meta_lic", "testdata/notice/container.zip.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/libb.so.meta_lic", "testdata/notice/lib/libc.a.meta_lic", "testdata/notice/lib/libd.so.meta_lic", }, }, { condition: "notice", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"application"}, firstParty{}, hr{}, library{"Device"}, usedBy{"application"}, notice{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/notice/NOTICE_LICENSE", "testdata/notice/application.meta_lic", "testdata/notice/bin/bin3.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/libb.so.meta_lic", }, }, { condition: "notice", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"bin/bin1"}, firstParty{}, hr{}, library{"Device"}, usedBy{"bin/bin1"}, library{"External"}, usedBy{"bin/bin1"}, notice{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/notice/NOTICE_LICENSE", "testdata/notice/bin/bin1.meta_lic", "testdata/notice/lib/liba.so.meta_lic", "testdata/notice/lib/libc.a.meta_lic", }, }, { condition: "notice", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []matcher{ hr{}, library{"External"}, usedBy{"lib/libd.so"}, notice{}, }, expectedDeps: []string{ "testdata/notice/NOTICE_LICENSE", "testdata/notice/lib/libd.so.meta_lic", }, }, { condition: "reciprocal", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/libb.so"}, firstParty{}, hr{}, library{"Device"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/lib/liba.so"}, library{"External"}, usedBy{"highest.apex/bin/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/highest.apex.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", }, }, { condition: "reciprocal", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"container.zip"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/bin2"}, usedBy{"container.zip/libb.so"}, firstParty{}, hr{}, library{"Device"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/liba.so"}, library{"External"}, usedBy{"container.zip/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/bin/bin2.meta_lic", "testdata/reciprocal/container.zip.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", "testdata/reciprocal/lib/libd.so.meta_lic", }, }, { condition: "reciprocal", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"application"}, firstParty{}, hr{}, library{"Device"}, usedBy{"application"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/reciprocal/application.meta_lic", "testdata/reciprocal/bin/bin3.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/libb.so.meta_lic", }, }, { condition: "reciprocal", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"bin/bin1"}, firstParty{}, hr{}, library{"Device"}, usedBy{"bin/bin1"}, library{"External"}, usedBy{"bin/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/reciprocal/bin/bin1.meta_lic", "testdata/reciprocal/lib/liba.so.meta_lic", "testdata/reciprocal/lib/libc.a.meta_lic", }, }, { condition: "reciprocal", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []matcher{ hr{}, library{"External"}, usedBy{"lib/libd.so"}, notice{}, }, expectedDeps: []string{ "testdata/notice/NOTICE_LICENSE", "testdata/reciprocal/lib/libd.so.meta_lic", }, }, { condition: "restricted", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/bin/bin2"}, firstParty{}, hr{}, library{"Android"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/libb.so"}, library{"Device"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/lib/liba.so"}, restricted{}, hr{}, library{"External"}, usedBy{"highest.apex/bin/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/restricted/RESTRICTED_LICENSE", "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/highest.apex.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", }, }, { condition: "restricted", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"container.zip"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/bin2"}, firstParty{}, hr{}, library{"Android"}, usedBy{"container.zip/bin2"}, usedBy{"container.zip/libb.so"}, library{"Device"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/liba.so"}, restricted{}, hr{}, library{"External"}, usedBy{"container.zip/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/restricted/RESTRICTED_LICENSE", "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/bin/bin2.meta_lic", "testdata/restricted/container.zip.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", "testdata/restricted/lib/libd.so.meta_lic", }, }, { condition: "restricted", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"application"}, firstParty{}, hr{}, library{"Device"}, usedBy{"application"}, restricted{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/restricted/RESTRICTED_LICENSE", "testdata/restricted/application.meta_lic", "testdata/restricted/bin/bin3.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/libb.so.meta_lic", }, }, { condition: "restricted", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"bin/bin1"}, firstParty{}, hr{}, library{"Device"}, usedBy{"bin/bin1"}, restricted{}, hr{}, library{"External"}, usedBy{"bin/bin1"}, reciprocal{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/reciprocal/RECIPROCAL_LICENSE", "testdata/restricted/RESTRICTED_LICENSE", "testdata/restricted/bin/bin1.meta_lic", "testdata/restricted/lib/liba.so.meta_lic", "testdata/restricted/lib/libc.a.meta_lic", }, }, { condition: "restricted", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []matcher{ hr{}, library{"External"}, usedBy{"lib/libd.so"}, notice{}, }, expectedDeps: []string{ "testdata/notice/NOTICE_LICENSE", "testdata/restricted/lib/libd.so.meta_lic", }, }, { condition: "proprietary", name: "apex", roots: []string{"highest.apex.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"highest.apex/bin/bin2"}, usedBy{"highest.apex/lib/libb.so"}, restricted{}, hr{}, library{"Android"}, usedBy{"highest.apex"}, usedBy{"highest.apex/bin/bin1"}, firstParty{}, hr{}, library{"Android"}, usedBy{"highest.apex/bin/bin2"}, library{"Device"}, usedBy{"highest.apex/bin/bin1"}, usedBy{"highest.apex/lib/liba.so"}, library{"External"}, usedBy{"highest.apex/bin/bin1"}, proprietary{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/proprietary/PROPRIETARY_LICENSE", "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/highest.apex.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "testdata/restricted/RESTRICTED_LICENSE", }, }, { condition: "proprietary", name: "container", roots: []string{"container.zip.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"container.zip/bin2"}, usedBy{"container.zip/libb.so"}, restricted{}, hr{}, library{"Android"}, usedBy{"container.zip"}, usedBy{"container.zip/bin1"}, firstParty{}, hr{}, library{"Android"}, usedBy{"container.zip/bin2"}, library{"Device"}, usedBy{"container.zip/bin1"}, usedBy{"container.zip/liba.so"}, library{"External"}, usedBy{"container.zip/bin1"}, proprietary{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/proprietary/PROPRIETARY_LICENSE", "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/bin/bin2.meta_lic", "testdata/proprietary/container.zip.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", "testdata/proprietary/lib/libd.so.meta_lic", "testdata/restricted/RESTRICTED_LICENSE", }, }, { condition: "proprietary", name: "application", roots: []string{"application.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"application"}, firstParty{}, hr{}, library{"Device"}, usedBy{"application"}, proprietary{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/proprietary/PROPRIETARY_LICENSE", "testdata/proprietary/application.meta_lic", "testdata/proprietary/bin/bin3.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/libb.so.meta_lic", }, }, { condition: "proprietary", name: "binary", roots: []string{"bin/bin1.meta_lic"}, expectedOut: []matcher{ hr{}, library{"Android"}, usedBy{"bin/bin1"}, firstParty{}, hr{}, library{"Device"}, usedBy{"bin/bin1"}, library{"External"}, usedBy{"bin/bin1"}, proprietary{}, }, expectedDeps: []string{ "testdata/firstparty/FIRST_PARTY_LICENSE", "testdata/proprietary/PROPRIETARY_LICENSE", "testdata/proprietary/bin/bin1.meta_lic", "testdata/proprietary/lib/liba.so.meta_lic", "testdata/proprietary/lib/libc.a.meta_lic", }, }, { condition: "proprietary", name: "library", roots: []string{"lib/libd.so.meta_lic"}, expectedOut: []matcher{ hr{}, library{"External"}, usedBy{"lib/libd.so"}, notice{}, }, expectedDeps: []string{ "testdata/notice/NOTICE_LICENSE", "testdata/proprietary/lib/libd.so.meta_lic", }, }, } for _, tt := range tests { t.Run(tt.condition+" "+tt.name, func(t *testing.T) { stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} rootFiles := make([]string, 0, len(tt.roots)) for _, r := range tt.roots { rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r) } var deps []string ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), tt.includeTOC, "", []string{tt.stripPrefix}, tt.title, &deps} err := htmlNotice(&ctx, rootFiles...) if err != nil { t.Fatalf("htmlnotice: error = %v, stderr = %v", err, stderr) return } if stderr.Len() > 0 { t.Errorf("htmlnotice: gotStderr = %v, want none", stderr) } t.Logf("got stdout: %s", stdout.String()) t.Logf("want stdout: %s", matcherList(tt.expectedOut).String()) out := bufio.NewScanner(stdout) lineno := 0 inBody := false hasTitle := false ttle, expectTitle := tt.expectedOut[0].(pageTitle) for out.Scan() { line := out.Text() if strings.TrimLeft(line, " ") == "" { continue } if !inBody { if expectTitle { if tl := checkTitle(line); len(tl) > 0 { if tl != ttle.t { t.Errorf("htmlnotice: unexpected title: got %q, want %q", tl, ttle.t) } hasTitle = true } } if bodyTag.MatchString(line) { inBody = true if expectTitle && !hasTitle { t.Errorf("htmlnotice: missing title: got no tag, want <title>%s", ttle.t) } } continue } if boilerPlate.MatchString(line) { continue } if len(tt.expectedOut) <= lineno { t.Errorf("htmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut)) } else if !tt.expectedOut[lineno].isMatch(line) { t.Errorf("htmlnotice: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String()) } lineno++ } if !inBody { t.Errorf("htmlnotice: missing body: got no tag, want tag followed by %s", matcherList(tt.expectedOut).String()) return } for ; lineno < len(tt.expectedOut); lineno++ { t.Errorf("htmlnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String()) } t.Logf("got deps: %q", deps) t.Logf("want deps: %q", tt.expectedDeps) if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) { t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n", strings.Join(w, "\n"), strings.Join(g, "\n")) } }) } } func checkTitle(line string) string { groups := titleTag.FindStringSubmatch(line) if len(groups) != 2 { return "" } return groups[1] } type matcher interface { isMatch(line string) bool String() string } type pageTitle struct { t string } func (m pageTitle) isMatch(line string) bool { groups := h1Tag.FindStringSubmatch(line) if len(groups) != 2 { return false } return groups[1] == html.EscapeString(m.t) } func (m pageTitle) String() string { return "

      " + html.EscapeString(m.t) + "

      " } type toc struct{} func (m toc) isMatch(line string) bool { return tocTag.MatchString(line) } func (m toc) String() string { return `
        ` } type target struct { name string } func (m target) isMatch(line string) bool { groups := installTarget.FindStringSubmatch(line) if len(groups) != 2 { return false } return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name)) } func (m target) String() string { return `
      • ` + html.EscapeString(m.name) + `` } type uses struct { name string } func (m uses) isMatch(line string) bool { groups := libReference.FindStringSubmatch(line) if len(groups) != 2 { return false } return groups[1] == html.EscapeString(m.name) } func (m uses) String() string { return `
      • ` + html.EscapeString(m.name) + `` } type hr struct{} func (m hr) isMatch(line string) bool { return horizontalRule.MatchString(line) } func (m hr) String() string { return "
        " } type library struct { name string } func (m library) isMatch(line string) bool { groups := libraryName.FindStringSubmatch(line) if len(groups) != 2 { return false } return groups[1] == html.EscapeString(m.name) } func (m library) String() string { return " " + html.EscapeString(m.name) + " used by:" } type usedBy struct { name string } func (m usedBy) isMatch(line string) bool { groups := usedByTarget.FindStringSubmatch(line) if len(groups) != 2 { return false } return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name)) } func (m usedBy) String() string { return "
      • out/.../" + html.EscapeString(m.name) } func matchesText(line, text string) bool { groups := licenseText.FindStringSubmatch(line) if len(groups) != 2 { return false } return groups[1] == html.EscapeString(text) } func expectedText(text string) string { return `
        ` + html.EscapeString(text)
        }
        
        type firstParty struct{}
        
        func (m firstParty) isMatch(line string) bool {
        	return matchesText(line, "&&&First Party License&&&")
        }
        
        func (m firstParty) String() string {
        	return expectedText("&&&First Party License&&&")
        }
        
        type notice struct{}
        
        func (m notice) isMatch(line string) bool {
        	return matchesText(line, "%%%Notice License%%%")
        }
        
        func (m notice) String() string {
        	return expectedText("%%%Notice License%%%")
        }
        
        type reciprocal struct{}
        
        func (m reciprocal) isMatch(line string) bool {
        	return matchesText(line, "$$$Reciprocal License$$$")
        }
        
        func (m reciprocal) String() string {
        	return expectedText("$$$Reciprocal License$$$")
        }
        
        type restricted struct{}
        
        func (m restricted) isMatch(line string) bool {
        	return matchesText(line, "###Restricted License###")
        }
        
        func (m restricted) String() string {
        	return expectedText("###Restricted License###")
        }
        
        type proprietary struct{}
        
        func (m proprietary) isMatch(line string) bool {
        	return matchesText(line, "@@@Proprietary License@@@")
        }
        
        func (m proprietary) String() string {
        	return expectedText("@@@Proprietary License@@@")
        }
        
        type matcherList []matcher
        
        func (l matcherList) String() string {
        	var sb strings.Builder
        	for _, m := range l {
        		s := m.String()
        		if s[:3] == s[len(s)-3:] {
        			fmt.Fprintln(&sb)
        		}
        		fmt.Fprintf(&sb, "%s\n", s)
        		if s[:3] == s[len(s)-3:] {
        			fmt.Fprintln(&sb)
        		}
        	}
        	return sb.String()
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/listshare/listshare.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"flag"
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"path/filepath"
        	"sort"
        	"strings"
        
        	"android/soong/response"
        	"android/soong/tools/compliance"
        )
        
        var (
        	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        	failNoLicenses    = fmt.Errorf("No licenses found")
        )
        
        func main() {
        	var expandedArgs []string
        	for _, arg := range os.Args[1:] {
        		if strings.HasPrefix(arg, "@") {
        			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        
        			respArgs, err := response.ReadRspFile(f)
        			f.Close()
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        			expandedArgs = append(expandedArgs, respArgs...)
        		} else {
        			expandedArgs = append(expandedArgs, arg)
        		}
        	}
        
        	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        
        	flags.Usage = func() {
        		fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} file.meta_lic {file.meta_lic...}
        
        Outputs a csv file with 1 project per line in the first field followed
        by target:condition pairs describing why the project must be shared.
        
        Each target is the path to a generated license metadata file for a
        Soong module or Make target, and the license condition is either
        restricted (e.g. GPL) or reciprocal (e.g. MPL).
        `, filepath.Base(os.Args[0]))
        	}
        
        	outputFile := flags.String("o", "-", "Where to write the list of projects to share. (default stdout)")
        
        	flags.Parse(expandedArgs)
        
        	// Must specify at least one root target.
        	if flags.NArg() == 0 {
        		flags.Usage()
        		os.Exit(2)
        	}
        
        	if len(*outputFile) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        		os.Exit(2)
        	} else {
        		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        		fi, err := os.Stat(dir)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        			os.Exit(1)
        		}
        		if !fi.IsDir() {
        			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        			os.Exit(1)
        		}
        	}
        
        	var ofile io.Writer
        	ofile = os.Stdout
        	var obuf *bytes.Buffer
        	if *outputFile != "-" {
        		obuf = &bytes.Buffer{}
        		ofile = obuf
        	}
        
        	err := listShare(ofile, os.Stderr, compliance.FS, flags.Args()...)
        	if err != nil {
        		if err == failNoneRequested {
        			flags.Usage()
        		}
        		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        		os.Exit(1)
        	}
        	if *outputFile != "-" {
        		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
        			os.Exit(1)
        		}
        	}
        	os.Exit(0)
        }
        
        // listShare implements the listshare utility.
        func listShare(stdout, stderr io.Writer, rootFS fs.FS, files ...string) error {
        	// Must be at least one root file.
        	if len(files) < 1 {
        		return failNoneRequested
        	}
        
        	// Read the license graph from the license metadata files (*.meta_lic).
        	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
        	if err != nil {
        		return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %v\n", files, os.Getenv("PWD"), err)
        	}
        	if licenseGraph == nil {
        		return failNoLicenses
        	}
        
        	// shareSource contains all source-sharing resolutions.
        	shareSource := compliance.ResolveSourceSharing(licenseGraph)
        
        	// Group the resolutions by project.
        	presolution := make(map[string]compliance.LicenseConditionSet)
        	for _, target := range shareSource.AttachesTo() {
        		if shareSource.IsPureAggregate(target) && !target.LicenseConditions().MatchesAnySet(compliance.ImpliesShared) {
        			continue
        		}
        		rl := shareSource.Resolutions(target)
        		sort.Sort(rl)
        		for _, r := range rl {
        			for _, p := range r.ActsOn().Projects() {
        				if _, ok := presolution[p]; !ok {
        					presolution[p] = r.Resolves()
        					continue
        				}
        				presolution[p] = presolution[p].Union(r.Resolves())
        			}
        		}
        	}
        
        	// Sort the projects for repeatability/stability.
        	projects := make([]string, 0, len(presolution))
        	for p := range presolution {
        		projects = append(projects, p)
        	}
        	sort.Strings(projects)
        
        	// Output the sorted projects and the source-sharing license conditions that each project resolves.
        	for _, p := range projects {
        		if presolution[p].IsEmpty() {
        			fmt.Fprintf(stdout, "%s\n", p)
        		} else {
        			fmt.Fprintf(stdout, "%s,%s\n", p, strings.Join(presolution[p].Names(), ","))
        		}
        	}
        
        	return nil
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/listshare/listshare_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"fmt"
        	"os"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance"
        )
        
        func TestMain(m *testing.M) {
        	// Change into the parent directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir(".."); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func Test(t *testing.T) {
        	type projectShare struct {
        		project    string
        		conditions []string
        	}
        	tests := []struct {
        		condition   string
        		name        string
        		outDir      string
        		roots       []string
        		expectedOut []projectShare
        	}{
        		{
        			condition:   "firstparty",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "notice",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "notice",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "notice",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "notice",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "notice",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition: "reciprocal",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "device/library",
        					conditions: []string{"reciprocal"},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        					},
        				},
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "device/library",
        					conditions: []string{"reciprocal"},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        					},
        				},
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "device/library",
        					conditions: []string{"reciprocal"},
        				},
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project: "device/library",
        					conditions: []string{
        						"reciprocal",
        					},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        					},
        				},
        			},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition: "restricted",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "base/library",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "device/library",
        					conditions: []string{"restricted_if_statically_linked"},
        				},
        				{
        					project:    "dynamic/binary",
        					conditions: []string{"restricted"},
        				},
        				{
        					project: "static/binary",
        					conditions: []string{
        						"restricted_if_statically_linked",
        					},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        						"restricted_if_statically_linked",
        					},
        				},
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "base/library",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "device/library",
        					conditions: []string{"restricted_if_statically_linked"},
        				},
        				{
        					project:    "dynamic/binary",
        					conditions: []string{"restricted"},
        				},
        				{
        					project: "static/binary",
        					conditions: []string{
        						"restricted_if_statically_linked",
        					},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        						"restricted_if_statically_linked",
        					},
        				},
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project: "device/library",
        					conditions: []string{
        						"restricted",
        						"restricted_if_statically_linked",
        					},
        				},
        				{
        					project: "distributable/application",
        					conditions: []string{
        						"restricted",
        						"restricted_if_statically_linked",
        					},
        				},
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project: "device/library",
        					conditions: []string{
        						"restricted_if_statically_linked",
        					},
        				},
        				{
        					project: "static/binary",
        					conditions: []string{
        						"restricted_if_statically_linked",
        					},
        				},
        				{
        					project: "static/library",
        					conditions: []string{
        						"reciprocal",
        						"restricted_if_statically_linked",
        					},
        				},
        			},
        		},
        		{
        			condition:   "restricted",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition: "proprietary",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "base/library",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "dynamic/binary",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "base/library",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "dynamic/binary",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "device/library",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "distributable/application",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition:   "proprietary",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition:   "proprietary",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []projectShare{},
        		},
        		{
        			condition: "regressgpl1",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "bin/threelibraries",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition: "regressgpl1",
        			name:      "containerplus",
        			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "bin/threelibraries",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/apache",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/c++",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition: "regressgpl2",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "bin/threelibraries",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/apache",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/c++",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/gpl",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        		{
        			condition: "regressgpl2",
        			name:      "containerplus",
        			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
        			expectedOut: []projectShare{
        				{
        					project:    "bin/threelibraries",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/apache",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/c++",
        					conditions: []string{"restricted"},
        				},
        				{
        					project:    "lib/gpl",
        					conditions: []string{"restricted"},
        				},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        			expectedOut := &bytes.Buffer{}
        			for _, p := range tt.expectedOut {
        				expectedOut.WriteString(p.project)
        				for _, lc := range p.conditions {
        					expectedOut.WriteString(",")
        					expectedOut.WriteString(lc)
        				}
        				expectedOut.WriteString("\n")
        			}
        
        			stdout := &bytes.Buffer{}
        			stderr := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        			}
        			err := listShare(stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)
        			if err != nil {
        				t.Fatalf("listshare: error = %v, stderr = %v", err, stderr)
        				return
        			}
        			if stderr.Len() > 0 {
        				t.Errorf("listshare: gotStderr = %v, want none", stderr)
        			}
        			out := stdout.String()
        			expected := expectedOut.String()
        			if out != expected {
        				outList := strings.Split(out, "\n")
        				expectedList := strings.Split(expected, "\n")
        				startLine := 0
        				for len(outList) > startLine && len(expectedList) > startLine && outList[startLine] == expectedList[startLine] {
        					startLine++
        				}
        				t.Errorf("listshare: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
        					out, expected, startLine+1, outList[startLine], expectedList[startLine])
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/rtrace/rtrace.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"flag"
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"path/filepath"
        	"sort"
        	"strings"
        
        	"android/soong/response"
        	"android/soong/tools/compliance"
        )
        
        var (
        	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        	failNoSources     = fmt.Errorf("\nNo projects or metadata files to trace back from")
        	failNoLicenses    = fmt.Errorf("No licenses found")
        )
        
        type context struct {
        	sources     []string
        	stripPrefix []string
        }
        
        func (ctx context) strip(installPath string) string {
        	for _, prefix := range ctx.stripPrefix {
        		if strings.HasPrefix(installPath, prefix) {
        			p := strings.TrimPrefix(installPath, prefix)
        			if 0 == len(p) {
        				continue
        			}
        			return p
        		}
        	}
        	return installPath
        }
        
        // newMultiString creates a flag that allows multiple values in an array.
        func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        	var f multiString
        	flags.Var(&f, name, usage)
        	return &f
        }
        
        // multiString implements the flag `Value` interface for multiple strings.
        type multiString []string
        
        func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        
        func main() {
        	var expandedArgs []string
        	for _, arg := range os.Args[1:] {
        		if strings.HasPrefix(arg, "@") {
        			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        
        			respArgs, err := response.ReadRspFile(f)
        			f.Close()
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        			expandedArgs = append(expandedArgs, respArgs...)
        		} else {
        			expandedArgs = append(expandedArgs, arg)
        		}
        	}
        
        	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        
        	flags.Usage = func() {
        		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        
        Calculates the source-sharing requirements in reverse starting at the
        -rtrace projects or metadata files that inherited source-sharing and
        working back to the targets where the source-sharing requirmements
        originate.
        
        Outputs a space-separated pair where the first field is an originating
        target with one or more restricted conditions and where the second
        field is a colon-separated list of the restricted conditions.
        
        Outputs a count of the originating targets, and if the count is zero,
        outputs a warning to check the -rtrace projects and/or filenames.
        
        Options:
        `, filepath.Base(os.Args[0]))
        		flags.PrintDefaults()
        	}
        
        	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
        	sources := newMultiString(flags, "rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
        	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        
        	flags.Parse(expandedArgs)
        
        	// Must specify at least one root target.
        	if flags.NArg() == 0 {
        		flags.Usage()
        		os.Exit(2)
        	}
        
        	if len(*sources) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "\nMust specify at least 1 --rtrace source.\n")
        		os.Exit(2)
        	}
        
        	if len(*outputFile) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        		os.Exit(2)
        	} else {
        		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        		fi, err := os.Stat(dir)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        			os.Exit(1)
        		}
        		if !fi.IsDir() {
        			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        			os.Exit(1)
        		}
        	}
        
        	var ofile io.Writer
        	ofile = os.Stdout
        	var obuf *bytes.Buffer
        	if *outputFile != "-" {
        		obuf = &bytes.Buffer{}
        		ofile = obuf
        	}
        
        	ctx := &context{
        		sources:     *sources,
        		stripPrefix: *stripPrefix,
        	}
        	_, err := traceRestricted(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
        	if err != nil {
        		if err == failNoneRequested {
        			flags.Usage()
        		}
        		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        		os.Exit(1)
        	}
        	if *outputFile != "-" {
        		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
        			os.Exit(1)
        		}
        	}
        	os.Exit(0)
        }
        
        // traceRestricted implements the rtrace utility.
        func traceRestricted(ctx *context, stdout, stderr io.Writer, rootFS fs.FS, files ...string) (*compliance.LicenseGraph, error) {
        	if len(files) < 1 {
        		return nil, failNoneRequested
        	}
        
        	if len(ctx.sources) < 1 {
        		return nil, failNoSources
        	}
        
        	// Read the license graph from the license metadata files (*.meta_lic).
        	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
        	if err != nil {
        		return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        	}
        	if licenseGraph == nil {
        		return nil, failNoLicenses
        	}
        
        	sourceMap := make(map[string]struct{})
        	for _, source := range ctx.sources {
        		sourceMap[source] = struct{}{}
        	}
        
        	compliance.TraceTopDownConditions(licenseGraph, func(tn *compliance.TargetNode) compliance.LicenseConditionSet {
        		if _, isPresent := sourceMap[tn.Name()]; isPresent {
        			return compliance.ImpliesRestricted
        		}
        		for _, project := range tn.Projects() {
        			if _, isPresent := sourceMap[project]; isPresent {
        				return compliance.ImpliesRestricted
        			}
        		}
        		return compliance.NewLicenseConditionSet()
        	})
        
        	// targetOut calculates the string to output for `target` adding `sep`-separated conditions as needed.
        	targetOut := func(target *compliance.TargetNode, sep string) string {
        		tOut := ctx.strip(target.Name())
        		return tOut
        	}
        
        	// outputResolution prints a resolution in the requested format to `stdout`, where one can read
        	// a resolution as `tname` resolves conditions named in `cnames`.
        	// `tname` is the name of the target the resolution traces back to.
        	// `cnames` is the list of conditions to resolve.
        	outputResolution := func(tname string, cnames []string) {
        		// ... one edge per line with names in a colon-separated tuple.
        		fmt.Fprintf(stdout, "%s %s\n", tname, strings.Join(cnames, ":"))
        	}
        
        	// Sort the resolutions by targetname for repeatability/stability.
        	actions := compliance.WalkResolutionsForCondition(licenseGraph, compliance.ImpliesShared).AllActions()
        	targets := make(compliance.TargetNodeList, 0, len(actions))
        	for tn := range actions {
        		if tn.LicenseConditions().MatchesAnySet(compliance.ImpliesRestricted) {
        			targets = append(targets, tn)
        		}
        	}
        	sort.Sort(targets)
        
        	// Output the sorted targets.
        	for _, target := range targets {
        		var tname string
        		tname = targetOut(target, ":")
        
        		// cnames accumulates the list of condition names originating at a single origin that apply to `target`.
        		cnames := target.LicenseConditions().Names()
        
        		// Output 1 line for each attachesTo+actsOn combination.
        		outputResolution(tname, cnames)
        	}
        	fmt.Fprintf(stdout, "restricted conditions trace to %d targets\n", len(targets))
        	if 0 == len(targets) {
        		fmt.Fprintln(stdout, "  (check for typos in project names or metadata files)")
        	}
        	return licenseGraph, nil
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/rtrace/rtrace_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"fmt"
        	"os"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance"
        )
        
        func TestMain(m *testing.M) {
        	// Change into the parent directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir(".."); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func Test_plaintext(t *testing.T) {
        	tests := []struct {
        		condition   string
        		name        string
        		outDir      string
        		roots       []string
        		ctx         context
        		expectedOut []string
        	}{
        		{
        			condition:   "firstparty",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition: "firstparty",
        			name:      "apex_trimmed",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/firstparty/bin/bin1.meta_lic"},
        				stripPrefix: []string{"testdata/firstparty/"},
        			},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "firstparty",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "notice",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition: "notice",
        			name:      "apex_trimmed",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/notice/bin/bin1.meta_lic"},
        				stripPrefix: []string{"testdata/notice/"},
        			},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "notice",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "notice",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "notice",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "notice",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition: "reciprocal",
        			name:      "apex_trimmed",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/reciprocal/bin/bin1.meta_lic"},
        				stripPrefix: []string{"testdata/reciprocal/"},
        			},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition: "restricted",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []string{
        				"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked",
        				"testdata/restricted/lib/libb.so.meta_lic restricted",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "apex_trimmed_bin1",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/restricted/bin/bin1.meta_lic"},
        				stripPrefix: []string{"testdata/restricted/"},
        			},
        			expectedOut: []string{"lib/liba.so.meta_lic restricted_if_statically_linked"},
        		},
        		{
        			condition: "restricted",
        			name:      "apex_trimmed_bin2",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/restricted/bin/bin2.meta_lic"},
        				stripPrefix: []string{"testdata/restricted/"},
        			},
        			expectedOut: []string{"lib/libb.so.meta_lic restricted"},
        		},
        		{
        			condition: "restricted",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []string{
        				"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked",
        				"testdata/restricted/lib/libb.so.meta_lic restricted",
        			},
        		},
        		{
        			condition:   "restricted",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked"},
        		},
        		{
        			condition:   "restricted",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"testdata/restricted/lib/liba.so.meta_lic restricted_if_statically_linked"},
        		},
        		{
        			condition:   "restricted",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "proprietary",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"testdata/proprietary/lib/libb.so.meta_lic restricted"},
        		},
        		{
        			condition: "proprietary",
        			name:      "apex_trimmed_bin1",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/proprietary/bin/bin1.meta_lic"},
        				stripPrefix: []string{"testdata/proprietary/"},
        			},
        			expectedOut: []string{},
        		},
        		{
        			condition: "proprietary",
        			name:      "apex_trimmed_bin2",
        			roots:     []string{"highest.apex.meta_lic"},
        			ctx: context{
        				sources:     []string{"testdata/proprietary/bin/bin2.meta_lic"},
        				stripPrefix: []string{"testdata/proprietary/"},
        			},
        			expectedOut: []string{"lib/libb.so.meta_lic restricted"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"testdata/proprietary/lib/libb.so.meta_lic restricted"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "proprietary",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{},
        		},
        		{
        			condition:   "proprietary",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        			expectedOut := &bytes.Buffer{}
        			for _, eo := range tt.expectedOut {
        				expectedOut.WriteString(eo)
        				expectedOut.WriteString("\n")
        			}
        			fmt.Fprintf(expectedOut, "restricted conditions trace to %d targets\n", len(tt.expectedOut))
        			if 0 == len(tt.expectedOut) {
        				fmt.Fprintln(expectedOut, "  (check for typos in project names or metadata files)")
        			}
        
        			stdout := &bytes.Buffer{}
        			stderr := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        			}
        			if len(tt.ctx.sources) < 1 {
        				tt.ctx.sources = rootFiles
        			}
        			_, err := traceRestricted(&tt.ctx, stdout, stderr, compliance.GetFS(tt.outDir), rootFiles...)
        			t.Logf("rtrace: stderr = %v", stderr)
        			t.Logf("rtrace: stdout = %v", stdout)
        			if err != nil {
        				t.Fatalf("rtrace: error = %v", err)
        				return
        			}
        			if stderr.Len() > 0 {
        				t.Errorf("rtrace: gotStderr = %v, want none", stderr)
        			}
        			out := stdout.String()
        			expected := expectedOut.String()
        			if out != expected {
        				outList := strings.Split(out, "\n")
        				expectedList := strings.Split(expected, "\n")
        				startLine := 0
        				for startLine < len(outList) && startLine < len(expectedList) && outList[startLine] == expectedList[startLine] {
        					startLine++
        				}
        				t.Errorf("rtrace: gotStdout = %v, want %v, somewhere near line %d Stdout = %v, want %v",
        					out, expected, startLine+1, outList[startLine], expectedList[startLine])
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/shippedlibs/shippedlibs.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"flag"
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"path/filepath"
        	"strings"
        
        	"android/soong/response"
        	"android/soong/tools/compliance"
        )
        
        var (
        	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        	failNoLicenses    = fmt.Errorf("No licenses found")
        )
        
        type context struct {
        	stdout io.Writer
        	stderr io.Writer
        	rootFS fs.FS
        }
        
        func main() {
        	var expandedArgs []string
        	for _, arg := range os.Args[1:] {
        		if strings.HasPrefix(arg, "@") {
        			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        
        			respArgs, err := response.ReadRspFile(f)
        			f.Close()
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        			expandedArgs = append(expandedArgs, respArgs...)
        		} else {
        			expandedArgs = append(expandedArgs, arg)
        		}
        	}
        
        	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        
        	outputFile := flags.String("o", "-", "Where to write the library list. (default stdout)")
        
        	flags.Usage = func() {
        		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        
        Outputs a list of libraries used in the shipped images.
        
        Options:
        `, filepath.Base(os.Args[0]))
        		flags.PrintDefaults()
        	}
        
        	err := flags.Parse(expandedArgs)
        	if err != nil {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "%v\n", err)
        	}
        
        	// Must specify at least one root target.
        	if flags.NArg() == 0 {
        		flags.Usage()
        		os.Exit(2)
        	}
        
        	if len(*outputFile) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        		os.Exit(2)
        	} else {
        		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        		fi, err := os.Stat(dir)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        			os.Exit(1)
        		}
        		if !fi.IsDir() {
        			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        			os.Exit(1)
        		}
        	}
        
        	var ofile io.Writer
        	ofile = os.Stdout
        	if *outputFile != "-" {
        		ofile = &bytes.Buffer{}
        	}
        
        	ctx := &context{ofile, os.Stderr, compliance.FS}
        
        	err = shippedLibs(ctx, flags.Args()...)
        	if err != nil {
        		if err == failNoneRequested {
        			flags.Usage()
        		}
        		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        		os.Exit(1)
        	}
        	if *outputFile != "-" {
        		err := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        	}
        	os.Exit(0)
        }
        
        // shippedLibs implements the shippedlibs utility.
        func shippedLibs(ctx *context, files ...string) error {
        	// Must be at least one root file.
        	if len(files) < 1 {
        		return failNoneRequested
        	}
        
        	// Read the license graph from the license metadata files (*.meta_lic).
        	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        	if err != nil {
        		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        	}
        	if licenseGraph == nil {
        		return failNoLicenses
        	}
        
        	// rs contains all notice resolutions.
        	rs := compliance.ResolveNotices(licenseGraph)
        
        	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        	if err != nil {
        		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        	}
        
        	for lib := range ni.Libraries() {
        		fmt.Fprintln(ctx.stdout, lib)
        	}
        	return nil
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/shippedlibs/shippedlibs_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bufio"
        	"bytes"
        	"fmt"
        	"os"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance"
        )
        
        func TestMain(m *testing.M) {
        	// Change into the parent directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir(".."); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func Test(t *testing.T) {
        	tests := []struct {
        		condition   string
        		name        string
        		outDir      string
        		roots       []string
        		expectedOut []string
        	}{
        		{
        			condition:   "firstparty",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"Android"},
        		},
        		{
        			condition:   "firstparty",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"Android"},
        		},
        		{
        			condition:   "firstparty",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"Android"},
        		},
        		{
        			condition:   "firstparty",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"Android"},
        		},
        		{
        			condition:   "firstparty",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{"Android"},
        		},
        		{
        			condition:   "notice",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "notice",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "notice",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"Android", "Device"},
        		},
        		{
        			condition:   "notice",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "notice",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{"External"},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"Android", "Device"},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "reciprocal",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{"External"},
        		},
        		{
        			condition:   "restricted",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "restricted",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "restricted",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"Android", "Device"},
        		},
        		{
        			condition:   "restricted",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "restricted",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{"External"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "apex",
        			roots:       []string{"highest.apex.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "container",
        			roots:       []string{"container.zip.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "application",
        			roots:       []string{"application.meta_lic"},
        			expectedOut: []string{"Android", "Device"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "binary",
        			roots:       []string{"bin/bin1.meta_lic"},
        			expectedOut: []string{"Android", "Device", "External"},
        		},
        		{
        			condition:   "proprietary",
        			name:        "library",
        			roots:       []string{"lib/libd.so.meta_lic"},
        			expectedOut: []string{"External"},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        			stdout := &bytes.Buffer{}
        			stderr := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        			}
        
        			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir)}
        
        			err := shippedLibs(&ctx, rootFiles...)
        			if err != nil {
        				t.Fatalf("shippedLibs: error = %v, stderr = %v", err, stderr)
        				return
        			}
        			if stderr.Len() > 0 {
        				t.Errorf("shippedLibs: gotStderr = %v, want none", stderr)
        			}
        
        			t.Logf("got stdout: %s", stdout.String())
        
        			t.Logf("want stdout: %s", strings.Join(tt.expectedOut, "\n"))
        
        			out := bufio.NewScanner(stdout)
        			lineno := 0
        			for out.Scan() {
        				line := out.Text()
        				if strings.TrimLeft(line, " ") == "" {
        					continue
        				}
        				if len(tt.expectedOut) <= lineno {
        					t.Errorf("shippedLibs: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        				} else if tt.expectedOut[lineno] != line {
        					t.Errorf("shippedLibs: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno])
        				}
        				lineno++
        			}
        			for ; lineno < len(tt.expectedOut); lineno++ {
        				t.Errorf("shippedLibs: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno])
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/README.md
        ================================================
        ## Test data
        
        Each non-regression directory under testdata/ defines a similar build graph.
        All have the same structure, but different versions of the graph have different
        license metadata.
        
        The regression* directories can have whatever structure is required for the
        specific test case.
        
        ### Testdata build graph structure:
        
        The structure is meant to simulate some common scenarios:
        
        *   a `lib/` directory with some libraries
        *   a `bin/` directory with some executables
        *   one of the binaries, `bin3`, is a toolchain executable like a compiler
        *   an `application` built with the `bin3` compiler and linking a couple libraries
        *   a pure aggregation `continer.zip` that merely bundles files together, and
        *   an apex file (more like an apk file) with some binaries and libraries.
        
        The testdata starts with a `firstparty/` version containng only first-party
        licenses, and each subsequent directory introduces more restrictive conditions:
        
        *   `notice/` starts with `firstparty/` adds third-party notice conditions
        *   `reciprocal/` starts with `notice/` and adds some reciprocal conditions
        *   `restricted/` starts with `reciprocal/` and adds some restricted conditions
        *   `proprietary/` starts with `restricted/` and add some privacy conditions
        
        #### a `lib/` directory with some libraries
        
        ```dot
        strict digraph {
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	libc [label="lib/libc.a.meta_lic"];
        	libd [label="lib/libd.so.meta_lic"];
        }
        ```
        
        #### a `bin/` directory with some executables
        
        strict digraph {
        	rankdir=LR;
        	bin1 [label="bin/bin1.meta_lic"];
        	bin2 [label="bin/bin2.meta_lic"];
        	bin3 [label="bin/bin3.meta_lic\ntoolchain"];
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	libc [label="lib/libc.a.meta_lic"];
        	libd [label="lib/libd.so.meta_lic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	{rank=same; bin1 bin2 bin3}
        }
        
        #### an `application` built with the `bin3` compiler and linking a couple libraries
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="application.meta_lic"];
        	bin3 [label="bin/bin3.meta_lic"];
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	{rank=same; app}
        }
        ```
        
        #### a pure aggregation `container.zip` that merely bundles files together
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	bin1 [label="bin/bin1.meta_lic"];
        	bin2 [label="bin/bin2.meta_lic"];
        	container [label="container.zip.meta_lic"];
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	libc [label="lib/libc.a.meta_lic"];
        	libd [label="lib/libd.so.meta_lic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	{rank=same; container}
        }
        ```
        
        #### an apex file (more like an apk file) with some binaries and libraries
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	apex [label="highest.apex.meta_lic"];
        	bin1 [label="bin/bin1.meta_lic"];
        	bin2 [label="bin/bin2.meta_lic"];
        	bin3 [label="bin/bin3.meta_lic"];
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	libc [label="lib/libc.a.meta_lic"];
        	libd [label="lib/libd.so.meta_lic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; apex}
        }
        ```
        
        #### the whole build graph
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	apex [label="highest.apex.meta_lic"];
        	app [label="application.meta_lic"];
        	bin1 [label="bin/bin1.meta_lic"];
        	bin2 [label="bin/bin2.meta_lic"];
        	bin3 [label="bin/bin3.meta_lic"];
        	container [label="container.zip.meta_lic"];
        	liba [label="lib/liba.so.meta_lic"];
        	libb [label="lib/libb.so.meta_lic"];
        	libc [label="lib/libc.a.meta_lic"];
        	libd [label="lib/libd.so.meta_lic"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        
        ### firstparty/ testdata starts with all first-party licensing
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="firstparty/application.meta_lic"];
        	bin1 [label="firstparty/bin/bin1.meta_lic"];
        	bin2 [label="firstparty/bin/bin2.meta_lic"];
        	bin3 [label="firstparty/bin/bin3.meta_lic"];
        	container [label="firstparty/container.zip.meta_lic"];
        	apex [label="firstparty/highest.apex.meta_lic"];
        	liba [label="firstparty/lib/liba.so.meta_lic"];
        	libb [label="firstparty/lib/libb.so.meta_lic"];
        	libc [label="firstparty/lib/libc.a.meta_lic"];
        	lib [label="firstparty/lib/libd.so.meta_lic"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        ### notice/ testdata introduces third-party notice conditions
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="notice/application.meta_lic"];
        	bin1 [label="notice/bin/bin1.meta_lic"];
        	bin2 [label="notice/bin/bin2.meta_lic"];
        	bin3 [label="notice/bin/bin3.meta_lic\nnotice"];
        	container [label="notice/container.zip.meta_lic"];
        	apex [label="notice/highest.apex.meta_lic"];
        	liba [label="notice/lib/liba.so.meta_lic\nnotice"];
        	libb [label="notice/lib/libb.so.meta_lic"];
        	libc [label="notice/lib/libc.a.meta_lic\nnotice"];
        	libd [label="notice/lib/libd.so.meta_lic\nnotice"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        ### reciprocal/ testdata introduces third-party reciprocal sharing conditions
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="reciprocal/application.meta_lic"];
        	bin1 [label="reciprocal/bin/bin1.meta_lic"];
        	bin2 [label="reciprocal/bin/bin2.meta_lic"];
        	bin3 [label="reciprocal/bin/bin3.meta_lic\nnotice"];
        	container [label="reciprocal/container.zip.meta_lic"];
        	apex [label="reciprocal/highest.apex.meta_lic"];
        	liba [label="reciprocal/lib/liba.so.meta_lic\nreciprocal"];
        	libb [label="reciprocal/lib/libb.so.meta_lic"];
        	libc [label="reciprocal/lib/libc.a.meta_lic\nreciprocal"];
        	libd [label="reciprocal/lib/libd.so.meta_lic\nnotice"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        ### restricted/ testdata introduces restricted source sharing conditions
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="restricted/application.meta_lic"];
        	bin1 [label="restricted/bin/bin1.meta_lic"];
        	bin2 [label="restricted/bin/bin2.meta_lic"];
        	bin3 [label="restricted/bin/bin3.meta_lic\nrestricted"];
        	container [label="restricted/container.zip.meta_lic"];
        	apex [label="restricted/highest.apex.meta_lic"];
        	liba [label="restricted/lib/liba.so.meta_lic\nrestricted"];
        	libb [label="restricted/lib/libb.so.meta_lic\nrestricted"];
        	libc [label="restricted/lib/libc.a.meta_lic\nreciprocal"];
        	libd [label="restricted/lib/libd.so.meta_lic\nnotice"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        ### proprietary/ testdata introduces privacy conditions
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	app [label="proprietary/application.meta_lic"];
        	bin1 [label="proprietary/bin/bin1.meta_lic"];
        	bin2 [label="proprietary/bin/bin2.meta_lic\nby_exception_only\nproprietary"];
        	bin3 [label="proprietary/bin/bin3.meta_lic\nrestricted"];
        	container [label="proprietary/container.zip.meta_lic"];
        	apex [label="proprietary/highest.apex.meta_lic"];
        	liba [label="proprietary/lib/liba.so.meta_lic\nby_exception_only\nproprietary"];
        	libb [label="proprietary/lib/libb.so.meta_lic\nrestricted"];
        	libc [label="proprietary/lib/libc.a.meta_lic\nby_exception_only\nproprietary"];
        	libd [label="proprietary/lib/libd.so.meta_lic\nnotice"];
        	app -> bin3 [label="toolchain"];
        	app -> liba [label="static"];
        	app -> libb [label="dynamic"];
        	bin1 -> liba [label="static"];
        	bin1 -> libc [label="static"];
        	bin2 -> libb [label="dynamic"];
        	bin2 -> libd [label="dynamic"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> liba [label="static"];
        	container -> libb [label="static"];
        	apex -> bin1 [label="static"];
        	apex -> bin2 [label="static"];
        	apex -> liba [label="static"];
        	apex -> libb [label="static"];
        	{rank=same; app container apex}
        }
        ```
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/FIRST_PARTY_LICENSE
        ================================================
        &&&First Party License&&&
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/METADATA
        ================================================
        # Comments are allowed
        name: "1ptd"
        description: "First Party Test Data"
        third_party {
            version: "1.0"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/application.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "distributable/application"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        installed:  "out/target/product/fictional/bin/application"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin3"
        deps:  {
          file:  "testdata/firstparty/bin/bin3.meta_lic"
          annotations:  "toolchain"
        }
        deps:  {
          file:  "testdata/firstparty/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "static/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libc.a"
        deps:  {
          file:  "testdata/firstparty/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/libc.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "dynamic/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/lib/libd.so"
        deps:  {
          file:  "testdata/firstparty/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/firstparty/lib/libd.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/bin/bin3.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "standalone/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/firstparty/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/highest.apex.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "highest/apex"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        installed:  "out/target/product/fictional/system/apex/highest.apex"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/liba.so"
          container_path:  "/lib/liba.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/lib/libb.so"
          container_path:  "/lib/libb.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin1"
          container_path:  "/bin/bin1"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin2"
          container_path:  "/bin/bin2"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/firstparty/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/firstparty/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/lib/liba.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "device/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        installed:  "out/target/product/fictional/system/lib/liba.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/lib/libb.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "base/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        installed:  "out/target/product/fictional/system/lib/libb.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/lib/libc.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "static/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/firstparty/lib/libd.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "dynamic/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        installed:  "out/target/product/fictional/system/lib/libd.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/METADATA
        ================================================
        # Comments are allowed
        name: "noticetd"
        description: "Notice Test Data"
        third_party {
            version: "1.0"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/NOTICE_LICENSE
        ================================================
        %%%Notice License%%%
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/application.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "distributable/application"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        installed:  "out/target/product/fictional/bin/application"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin3"
        deps:  {
          file:  "testdata/notice/bin/bin3.meta_lic"
          annotations:  "toolchain"
        }
        deps:  {
          file:  "testdata/notice/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "static/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libc.a"
        deps:  {
          file:  "testdata/notice/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/libc.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "dynamic/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/lib/libd.so"
        deps:  {
          file:  "testdata/notice/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/notice/lib/libd.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "standalone/binary"
        license_kinds:  "SPDX-license-identifier-NCSA"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/notice/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/highest.apex.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "highest/apex"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        installed:  "out/target/product/fictional/system/apex/highest.apex"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/liba.so"
          container_path:  "/lib/liba.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/lib/libb.so"
          container_path:  "/lib/libb.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin1"
          container_path:  "/bin/bin1"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin2"
          container_path:  "/bin/bin2"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/notice/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/notice/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/lib/liba.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "device/library"
        license_kinds:  "SPDX-license-identifier-BSD"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        installed:  "out/target/product/fictional/system/lib/liba.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/lib/libb.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "base/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        installed:  "out/target/product/fictional/system/lib/libb.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/lib/libc.a.meta_lic
        ================================================
        package_name:  "External"
        projects:  "static/library"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/notice/lib/libd.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "dynamic/library"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        installed:  "out/target/product/fictional/system/lib/libd.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/METADATA
        ================================================
        # comments are allowed
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/PROPRIETARY_LICENSE
        ================================================
        @@@Proprietary License@@@
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/application.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "distributable/application"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        installed:  "out/target/product/fictional/bin/application"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin3"
        deps:  {
          file:  "testdata/proprietary/bin/bin3.meta_lic"
          annotations:  "toolchain"
        }
        deps:  {
          file:  "testdata/proprietary/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "static/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libc.a"
        deps:  {
          file:  "testdata/proprietary/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/libc.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "dynamic/binary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        license_conditions:  "by_exception_only"
        license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/lib/libd.so"
        deps:  {
          file:  "testdata/proprietary/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/proprietary/lib/libd.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "standalone/binary"
        license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        license_conditions:  "restricted_if_statically_linked"
        license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/proprietary/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/highest.apex.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "highest/apex"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        installed:  "out/target/product/fictional/system/apex/highest.apex"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/liba.so"
          container_path:  "/lib/liba.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/lib/libb.so"
          container_path:  "/lib/libb.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin1"
          container_path:  "/bin/bin1"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin2"
          container_path:  "/bin/bin2"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/proprietary/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/proprietary/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/lib/liba.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "device/library"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        license_conditions:  "by_exception_only"
        license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        installed:  "out/target/product/fictional/system/lib/liba.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/lib/libb.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "base/library"
        license_kinds:  "SPDX-license-identifier-GPL-2.0"
        license_conditions:  "restricted"
        license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        installed:  "out/target/product/fictional/system/lib/libb.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/lib/libc.a.meta_lic
        ================================================
        package_name:  "External"
        projects:  "static/library"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        license_conditions:  "by_exception_only"
        license_texts:  "testdata/proprietary/PROPRIETARY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/proprietary/lib/libd.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "dynamic/library"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        installed:  "out/target/product/fictional/system/lib/libd.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/METADATA
        ================================================
        # Comments are allowed
        description: "Reciprocal Test Data"
        third_party {
            version: "1.0"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/RECIPROCAL_LICENSE
        ================================================
        $$$Reciprocal License$$$
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/application.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "distributable/application"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        installed:  "out/target/product/fictional/bin/application"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin3"
        deps:  {
          file:  "testdata/reciprocal/bin/bin3.meta_lic"
          annotations:  "toolchain"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "static/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libc.a"
        deps:  {
          file:  "testdata/reciprocal/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/libc.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "dynamic/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/lib/libd.so"
        deps:  {
          file:  "testdata/reciprocal/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/libd.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "standalone/binary"
        license_kinds:  "SPDX-license-identifier-NCSA"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/reciprocal/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/highest.apex.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "highest/apex"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        installed:  "out/target/product/fictional/system/apex/highest.apex"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/liba.so"
          container_path:  "/lib/liba.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/lib/libb.so"
          container_path:  "/lib/libb.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin1"
          container_path:  "/bin/bin1"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin2"
          container_path:  "/bin/bin2"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/reciprocal/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/reciprocal/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/lib/liba.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "device/library"
        license_kinds:  "SPDX-license-identifier-MPL"
        license_conditions:  "reciprocal"
        license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        installed:  "out/target/product/fictional/system/lib/liba.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/lib/libb.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "base/library"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        installed:  "out/target/product/fictional/system/lib/libb.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/lib/libc.a.meta_lic
        ================================================
        package_name:  "External"
        projects:  "static/library"
        license_kinds:  "SPDX-license-identifier-MPL"
        license_conditions:  "reciprocal"
        license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/reciprocal/lib/libd.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "dynamic/library"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        installed:  "out/target/product/fictional/system/lib/libd.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/README.md
        ================================================
        ## Start long walks followed by short walks
        
        Detect concurrency error where "already started" treated as
        "already finished".
        
        ### Testdata build graph structure:
        
        A restricted licensed library sandwiched between a notice library and a notice
        binary. The source-code for the libraries only needs to be shared if shipped
        alongside the container with the binaries.
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	bin1 [label="bin/bin1.meta_lic\nproprietary"];
        	bin2 [label="bin/bin2.meta_lic\nproprietary"];
        	bin3 [label="bin/bin3.meta_lic\nproprietary"];
        	bin4 [label="bin/bin4.meta_lic\nproprietary"];
        	bin5 [label="bin/bin5.meta_lic\nproprietary"];
        	bin6 [label="bin/bin6.meta_lic\nproprietary"];
        	bin7 [label="bin/bin7.meta_lic\nproprietary"];
        	bin8 [label="bin/bin8.meta_lic\nproprietary"];
        	bin9 [label="bin/bin9.meta_lic\nproprietary"];
        	container [label="container.zip.meta_lic\nnotice"];
        	lib1 [label="lib/lib1.so.meta_lic\nnotice"];
        	lib2 [label="lib/lib2.so.meta_lic\nnotice"];
        	lib3 [label="lib/lib3.so.meta_lic\nnotice"];
        	lib4 [label="lib/lib4.so.meta_lic\nnotice"];
        	lib5 [label="lib/lib5.so.meta_lic\nnotice"];
        	lib6 [label="lib/lib6.so.meta_lic\nnotice"];
        	lib7 [label="lib/lib7.so.meta_lic\nnotice"];
        	lib8 [label="lib/lib8.so.meta_lic\nnotice"];
        	lib9 [label="lib/lib9.so.meta_lic\nrestricted"];
        	container -> bin1 [label="static"];
        	container -> bin2 [label="static"];
        	container -> bin3 [label="static"];
        	container -> bin4 [label="static"];
        	container -> bin5 [label="static"];
        	container -> bin6 [label="static"];
        	container -> bin7 [label="static"];
        	container -> bin8 [label="static"];
        	container -> bin9 [label="static"];
        	bin1 -> lib1 [label="static"];
        	bin2 -> lib2 [label="static"];
        	bin3 -> lib3 [label="static"];
        	bin4 -> lib4 [label="static"];
        	bin5 -> lib5 [label="static"];
        	bin6 -> lib6 [label="static"];
        	bin7 -> lib7 [label="static"];
        	bin8 -> lib8 [label="static"];
        	bin9 -> lib9 [label="static"];
        	lib1 -> lib2 [label="static"];
        	lib2 -> lib3 [label="static"];
        	lib3 -> lib4 [label="static"];
        	lib4 -> lib5 [label="static"];
        	lib5 -> lib6 [label="static"];
        	lib6 -> lib7 [label="static"];
        	lib7 -> lib8 [label="static"];
        	lib8 -> lib9 [label="static"];
        	{rank=same; container}
        }
        ```
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/lib1.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib1.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/lib2.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib2.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin3.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/lib/lib3.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib3.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin4.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin4"
        installed:  "out/target/product/fictional/system/bin/bin4"
        sources:  "out/target/product/fictional/system/lib/lib4.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib4.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin5.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin5"
        installed:  "out/target/product/fictional/system/bin/bin5"
        sources:  "out/target/product/fictional/system/lib/lib5.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib5.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin6.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin6"
        installed:  "out/target/product/fictional/system/bin/bin6"
        sources:  "out/target/product/fictional/system/lib/lib6.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib6.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin7.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin7"
        installed:  "out/target/product/fictional/system/bin/bin7"
        sources:  "out/target/product/fictional/system/lib/lib7.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib7.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin8.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin8"
        installed:  "out/target/product/fictional/system/bin/bin8"
        sources:  "out/target/product/fictional/system/lib/lib8.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib8.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/bin/bin9.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "legacy_proprietary"
        license_conditions:  "proprietary"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin9"
        installed:  "out/target/product/fictional/system/bin/bin9"
        sources:  "out/target/product/fictional/system/lib/lib9.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib9.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/bin/bin4"
        sources:  "out/target/product/fictional/system/bin/bin5"
        sources:  "out/target/product/fictional/system/bin/bin6"
        sources:  "out/target/product/fictional/system/bin/bin7"
        sources:  "out/target/product/fictional/system/bin/bin8"
        sources:  "out/target/product/fictional/system/bin/bin9"
        deps:  {
          file:  "testdata/regressconcur/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin3.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin4.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin5.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin6.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin7.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin8.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressconcur/bin/bin9.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib1.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib1.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib1.a"
        installed:  "out/target/product/fictional/system/lib/lib1.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib2.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib2.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib2.a"
        installed:  "out/target/product/fictional/system/lib/lib2.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib3.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib3.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib3.a"
        installed:  "out/target/product/fictional/system/lib/lib3.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib4.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib4.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib4.a"
        installed:  "out/target/product/fictional/system/lib/lib4.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib5.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib5.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib5.a"
        installed:  "out/target/product/fictional/system/lib/lib5.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib6.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib6.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib6.a"
        installed:  "out/target/product/fictional/system/lib/lib6.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib7.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib7.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib7.a"
        installed:  "out/target/product/fictional/system/lib/lib7.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib8.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib8.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib8.a"
        installed:  "out/target/product/fictional/system/lib/lib8.so"
        sources:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.a"
        deps:  {
          file:  "testdata/regressconcur/lib/lib9.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressconcur/lib/lib9.a.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/restricted"
        license_kinds:  "SPDX-license-identifier-GPL-2.0"
        license_conditions:  "restricted"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/lib9.a"
        installed:  "out/target/product/fictional/system/lib/lib9.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/README.md
        ================================================
        ## Shipped versus non-shipped libraries with restricted license
        
        ### Testdata build graph structure:
        
        A restricted licensed library sandwiched between a notice library and a notice
        binary. The source-code for the libraries only needs to be shared if shipped
        alongside the container with the binaries.
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	bin1 [label="bin/bin1.meta_lic\nnotice"];
        	bin2 [label="bin/bin2.meta_lic\nnotice"];
        	bin3 [label="bin/bin3.meta_lic\nnotice"];
        	container [label="container.zip.meta_lic\nnotice"];
        	libapache [label="lib/libapache.so.meta_lic\nnotice"];
        	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
        	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
        	container -> bin1[label="static"];
        	container -> bin2 [label="static"];
        	container -> bin3 [label="static"];
        	bin1 -> libcxx [label="dynamic"];
        	bin2 -> libapache [label="dynamic"];
        	bin2 -> libcxx [label="dynamic"];
        	bin3 -> libapache [label="dynamic"];
        	bin3 -> libcxx [label="dynamic"];
        	bin3 -> libgpl [label="dynamic"];
        	libapache -> libcxx [label="dynamic"];
        	libgpl -> libcxx [label="dynamic"];
        	{rank=same; container}
        }
        ```
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/twolibraries"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        deps:  {
          file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "bin/threelibraries"
        license_kinds:  "SPDX-license-identifier-NCSA"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        sources:  "out/target/product/fictional/system/lib/libgpl.so"
        deps:  {
          file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl1/lib/libgpl.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        deps:  {
          file:  "testdata/regressgpl1/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl1/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl1/bin/bin3.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/apache"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
        installed:  "out/target/product/fictional/system/lib/libapache.so"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "lib/c++"
        license_kinds:  "SPDX-license-identifier-BSD"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
        installed:  "out/target/product/fictional/system/lib/libc++.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "lib/gpl"
        license_kinds:  "SPDX-license-identifier-GPL-2.0"
        license_conditions:  "restricted"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
        installed:  "out/target/product/fictional/system/lib/libgpl.so"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/README.md
        ================================================
        ## Libraries shipped inside container with restricted license
        
        ### Testdata build graph structure:
        
        A restricted licensed library sandwiched between a notice library and a notice
        binary. The source-code for the libraries needs to be shared when shipped as
        part of the container with the binaries.
        
        ```dot
        strict digraph {
        	rankdir=LR;
        	bin1 [label="bin/bin1.meta_lic\nnotice"];
        	bin2 [label="bin/bin2.meta_lic\nnotice"];
        	bin3 [label="bin/bin3.meta_lic\nnotice"];
        	container [label="container.zip.meta_lic\nnotice"];
        	libapache [label="lib/libapache.so.meta_lic\nnotice"];
        	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
        	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
        	container -> bin1[label="static"];
        	container -> bin2 [label="static"];
        	container -> bin3 [label="static"];
        	container -> libapache [label="static"];
        	container -> libcxx [label="static"];
        	container -> libgpl [label="static"];
        	bin1 -> libcxx [label="dynamic"];
        	bin2 -> libapache [label="dynamic"];
        	bin2 -> libcxx [label="dynamic"];
        	bin3 -> libapache [label="dynamic"];
        	bin3 -> libcxx [label="dynamic"];
        	bin3 -> libgpl [label="dynamic"];
        	libapache -> libcxx [label="dynamic"];
        	libgpl -> libcxx [label="dynamic"];
        	{rank=same; container}
        }
        ```
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/onelibrary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "bin/twolibraries"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "bin/threelibraries"
        license_kinds:  "SPDX-license-identifier-NCSA"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        sources:  "out/target/product/fictional/system/lib/libgpl.so"
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/bin/bin3"
        sources:  "out/target/product/fictional/system/lib/libapache.so"
        deps:  {
          file:  "testdata/regressgpl2/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl2/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl2/bin/bin3.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "lib/apache"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "build/soong/licenses/LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
        installed:  "out/target/product/fictional/system/lib/libapache.so"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "lib/c++"
        license_kinds:  "SPDX-license-identifier-BSD"
        license_conditions:  "notice"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
        installed:  "out/target/product/fictional/system/lib/libc++.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "lib/gpl"
        license_kinds:  "SPDX-license-identifier-GPL-2.0"
        license_conditions:  "restricted"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
        installed:  "out/target/product/fictional/system/lib/libgpl.so"
        sources:  "out/target/product/fictional/system/lib/libc++.so"
        deps:  {
          file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/METADATA
        ================================================
        name {
            id: 1
        }
        third_party {
            version: 2
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/METADATA.android
        ================================================
        # Comments are allowed
        name: "testdata"
        description: "Restricted Test Data"
        third_party {
            version: "1.0"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/RESTRICTED_LICENSE
        ================================================
        ###Restricted License###
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/application.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "distributable/application"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/application_intermediates/application"
        installed:  "out/target/product/fictional/bin/application"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin3"
        deps:  {
          file:  "testdata/restricted/bin/bin3.meta_lic"
          annotations:  "toolchain"
        }
        deps:  {
          file:  "testdata/restricted/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/bin/bin1.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "static/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
        installed:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/lib/liba.a"
        sources:  "out/target/product/fictional/system/lib/libc.a"
        deps:  {
          file:  "testdata/restricted/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/libc.a.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/bin/bin2.meta_lic
        ================================================
        package_name:  "Android"
        module_classes: "EXECUTABLES"
        projects:  "dynamic/binary"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
        installed:  "out/target/product/fictional/system/bin/bin2"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/lib/libd.so"
        deps:  {
          file:  "testdata/restricted/lib/libb.so.meta_lic"
          annotations:  "dynamic"
        }
        deps:  {
          file:  "testdata/restricted/lib/libd.so.meta_lic"
          annotations:  "dynamic"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/bin/bin3.meta_lic
        ================================================
        package_name:  "Compiler"
        module_classes: "EXECUTABLES"
        projects:  "standalone/binary"
        license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        license_conditions:  "restricted_if_statically_linked"
        license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
        installed:  "out/target/product/fictional/system/bin/bin3"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/container.zip.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "container/zip"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
        installed:  "out/target/product/fictional/data/container.zip"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/"
          container_path:  "/"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/"
          container_path:  "/"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/restricted/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/highest.apex.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "highest/apex"
        license_kinds:  "SPDX-license-identifier-Apache-2.0"
        license_conditions:  "notice"
        license_texts:  "testdata/firstparty/FIRST_PARTY_LICENSE"
        is_container:  true
        built:  "out/target/product/fictional/obj/ETC/highest_intermediates/highest.apex"
        installed:  "out/target/product/fictional/system/apex/highest.apex"
        install_map {
          from_path:  "out/target/product/fictional/system/lib/liba.so"
          container_path:  "/lib/liba.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/lib/libb.so"
          container_path:  "/lib/libb.so"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin1"
          container_path:  "/bin/bin1"
        }
        install_map {
          from_path:  "out/target/product/fictional/system/bin/bin2"
          container_path:  "/bin/bin2"
        }
        sources:  "out/target/product/fictional/system/lib/liba.so"
        sources:  "out/target/product/fictional/system/lib/libb.so"
        sources:  "out/target/product/fictional/system/bin/bin1"
        sources:  "out/target/product/fictional/system/bin/bin2"
        deps:  {
          file:  "testdata/restricted/bin/bin1.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/bin/bin2.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/liba.so.meta_lic"
          annotations:  "static"
        }
        deps:  {
          file:  "testdata/restricted/lib/libb.so.meta_lic"
          annotations:  "static"
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/lib/liba.so.meta_lic
        ================================================
        package_name:  "Device"
        projects:  "device/library"
        license_kinds:  "SPDX-license-identifier-LGPL-2.0"
        license_conditions:  "restricted_if_statically_linked"
        license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/liba.a"
        installed:  "out/target/product/fictional/system/lib/liba.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/lib/libb.so.meta_lic
        ================================================
        package_name:  "Android"
        projects:  "base/library"
        license_kinds:  "SPDX-license-identifier-GPL-2.0"
        license_conditions:  "restricted"
        license_texts:  "testdata/restricted/RESTRICTED_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.so"
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libb.a"
        installed:  "out/target/product/fictional/system/lib/libb.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/lib/libc.a.meta_lic
        ================================================
        package_name:  "External"
        projects:  "static/library"
        license_kinds:  "SPDX-license-identifier-MPL"
        license_conditions:  "reciprocal"
        license_texts:  "testdata/reciprocal/RECIPROCAL_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc.a"
        
        
        ================================================
        FILE: tools/compliance/cmd/testdata/restricted/lib/libd.so.meta_lic
        ================================================
        package_name:  "External"
        projects:  "dynamic/library"
        license_kinds:  "SPDX-license-identifier-MIT"
        license_conditions:  "notice"
        license_texts:  "testdata/notice/NOTICE_LICENSE"
        is_container:  false
        built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libd.so"
        installed:  "out/target/product/fictional/system/lib/libd.so"
        
        
        ================================================
        FILE: tools/compliance/cmd/textnotice/textnotice.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"compress/gzip"
        	"flag"
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"path/filepath"
        	"sort"
        	"strings"
        
        	"android/soong/response"
        	"android/soong/tools/compliance"
        
        	"github.com/google/blueprint/deptools"
        )
        
        var (
        	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        	failNoLicenses    = fmt.Errorf("No licenses found")
        )
        
        type context struct {
        	stdout      io.Writer
        	stderr      io.Writer
        	rootFS      fs.FS
        	product     string
        	stripPrefix []string
        	title       string
        	deps        *[]string
        }
        
        func (ctx context) strip(installPath string) string {
        	for _, prefix := range ctx.stripPrefix {
        		if strings.HasPrefix(installPath, prefix) {
        			p := strings.TrimPrefix(installPath, prefix)
        			if 0 == len(p) {
        				p = ctx.product
        			}
        			if 0 == len(p) {
        				continue
        			}
        			return p
        		}
        	}
        	return installPath
        }
        
        // newMultiString creates a flag that allows multiple values in an array.
        func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        	var f multiString
        	flags.Var(&f, name, usage)
        	return &f
        }
        
        // multiString implements the flag `Value` interface for multiple strings.
        type multiString []string
        
        func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        
        func main() {
        	var expandedArgs []string
        	for _, arg := range os.Args[1:] {
        		if strings.HasPrefix(arg, "@") {
        			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        
        			respArgs, err := response.ReadRspFile(f)
        			f.Close()
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        			expandedArgs = append(expandedArgs, respArgs...)
        		} else {
        			expandedArgs = append(expandedArgs, arg)
        		}
        	}
        
        	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        
        	flags.Usage = func() {
        		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        
        Outputs a text NOTICE file.
        
        Options:
        `, filepath.Base(os.Args[0]))
        		flags.PrintDefaults()
        	}
        
        	outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
        	depsFile := flags.String("d", "", "Where to write the deps file")
        	product := flags.String("product", "", "The name of the product for which the notice is generated.")
        	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        	title := flags.String("title", "", "The title of the notice file.")
        
        	flags.Parse(expandedArgs)
        
        	// Must specify at least one root target.
        	if flags.NArg() == 0 {
        		flags.Usage()
        		os.Exit(2)
        	}
        
        	if len(*outputFile) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        		os.Exit(2)
        	} else {
        		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        		fi, err := os.Stat(dir)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        			os.Exit(1)
        		}
        		if !fi.IsDir() {
        			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        			os.Exit(1)
        		}
        	}
        
        	var ofile io.Writer
        	var closer io.Closer
        	ofile = os.Stdout
        	var obuf *bytes.Buffer
        	if *outputFile != "-" {
        		obuf = &bytes.Buffer{}
        		ofile = obuf
        	}
        	if strings.HasSuffix(*outputFile, ".gz") {
        		ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)
        		closer = ofile.(io.Closer)
        	}
        
        	var deps []string
        
        	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
        
        	err := textNotice(ctx, flags.Args()...)
        	if err != nil {
        		if err == failNoneRequested {
        			flags.Usage()
        		}
        		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        		os.Exit(1)
        	}
        	if closer != nil {
        		closer.Close()
        	}
        
        	if *outputFile != "-" {
        		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        	}
        	if *depsFile != "" {
        		err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
        			os.Exit(1)
        		}
        	}
        	os.Exit(0)
        }
        
        // textNotice implements the textNotice utility.
        func textNotice(ctx *context, files ...string) error {
        	// Must be at least one root file.
        	if len(files) < 1 {
        		return failNoneRequested
        	}
        
        	// Read the license graph from the license metadata files (*.meta_lic).
        	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        	if err != nil {
        		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        	}
        	if licenseGraph == nil {
        		return failNoLicenses
        	}
        
        	// rs contains all notice resolutions.
        	rs := compliance.ResolveNotices(licenseGraph)
        
        	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        	if err != nil {
        		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        	}
        
        	if len(ctx.title) > 0 {
        		fmt.Fprintf(ctx.stdout, "%s\n\n", ctx.title)
        	}
        	for h := range ni.Hashes() {
        		fmt.Fprintln(ctx.stdout, "==============================================================================")
        		for _, libName := range ni.HashLibs(h) {
        			fmt.Fprintf(ctx.stdout, "%s used by:\n", libName)
        			for _, installPath := range ni.HashLibInstalls(h, libName) {
        				fmt.Fprintf(ctx.stdout, "  %s\n", ctx.strip(installPath))
        			}
        			fmt.Fprintln(ctx.stdout)
        		}
        		ctx.stdout.Write(ni.HashText(h))
        		fmt.Fprintln(ctx.stdout)
        	}
        
        	*ctx.deps = ni.InputFiles()
        	sort.Strings(*ctx.deps)
        
        	return nil
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/textnotice/textnotice_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bufio"
        	"bytes"
        	"fmt"
        	"os"
        	"reflect"
        	"regexp"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance"
        )
        
        var (
        	horizontalRule = regexp.MustCompile("^===[=]*===$")
        )
        
        func TestMain(m *testing.M) {
        	// Change into the parent directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir(".."); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func Test(t *testing.T) {
        	tests := []struct {
        		condition    string
        		name         string
        		outDir       string
        		roots        []string
        		stripPrefix  string
        		expectedOut  []matcher
        		expectedDeps []string
        	}{
        		{
        			condition: "firstparty",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/bin/bin2"},
        				usedBy{"highest.apex/lib/liba.so"},
        				usedBy{"highest.apex/lib/libb.so"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/bin/bin2.meta_lic",
        				"testdata/firstparty/highest.apex.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/bin2"},
        				usedBy{"container.zip/liba.so"},
        				usedBy{"container.zip/libb.so"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/bin/bin2.meta_lic",
        				"testdata/firstparty/container.zip.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"application"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/application.meta_lic",
        				"testdata/firstparty/bin/bin3.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"bin/bin1"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"lib/libd.so"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/bin/bin2"},
        				usedBy{"highest.apex/lib/libb.so"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/lib/liba.so"},
        				library{"External"},
        				usedBy{"highest.apex/bin/bin1"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/bin2"},
        				usedBy{"container.zip/libb.so"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/liba.so"},
        				library{"External"},
        				usedBy{"container.zip/bin1"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"application"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"application"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"bin/bin1"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"bin/bin1"},
        				library{"External"},
        				usedBy{"bin/bin1"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"External"},
        				usedBy{"lib/libd.so"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/bin/bin2"},
        				usedBy{"highest.apex/lib/libb.so"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/lib/liba.so"},
        				library{"External"},
        				usedBy{"highest.apex/bin/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/bin/bin2.meta_lic",
        				"testdata/reciprocal/highest.apex.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/bin2"},
        				usedBy{"container.zip/libb.so"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/liba.so"},
        				library{"External"},
        				usedBy{"container.zip/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/bin/bin2.meta_lic",
        				"testdata/reciprocal/container.zip.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"application"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"application"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/application.meta_lic",
        				"testdata/reciprocal/bin/bin3.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"bin/bin1"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"bin/bin1"},
        				library{"External"},
        				usedBy{"bin/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"External"},
        				usedBy{"lib/libd.so"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/bin/bin2"},
        				firstParty{},
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex/bin/bin2"},
        				usedBy{"highest.apex/lib/libb.so"},
        				library{"Device"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/lib/liba.so"},
        				restricted{},
        				hr{},
        				library{"External"},
        				usedBy{"highest.apex/bin/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/bin/bin2.meta_lic",
        				"testdata/restricted/highest.apex.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/bin2"},
        				firstParty{},
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip/bin2"},
        				usedBy{"container.zip/libb.so"},
        				library{"Device"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/liba.so"},
        				restricted{},
        				hr{},
        				library{"External"},
        				usedBy{"container.zip/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/bin/bin2.meta_lic",
        				"testdata/restricted/container.zip.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"application"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"application"},
        				restricted{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/application.meta_lic",
        				"testdata/restricted/bin/bin3.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"bin/bin1"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"bin/bin1"},
        				restricted{},
        				hr{},
        				library{"External"},
        				usedBy{"bin/bin1"},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"External"},
        				usedBy{"lib/libd.so"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex/bin/bin2"},
        				usedBy{"highest.apex/lib/libb.so"},
        				restricted{},
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex"},
        				usedBy{"highest.apex/bin/bin1"},
        				firstParty{},
        				hr{},
        				library{"Android"},
        				usedBy{"highest.apex/bin/bin2"},
        				library{"Device"},
        				usedBy{"highest.apex/bin/bin1"},
        				usedBy{"highest.apex/lib/liba.so"},
        				library{"External"},
        				usedBy{"highest.apex/bin/bin1"},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/bin/bin2.meta_lic",
        				"testdata/proprietary/highest.apex.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        				"testdata/restricted/RESTRICTED_LICENSE",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip/bin2"},
        				usedBy{"container.zip/libb.so"},
        				restricted{},
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip"},
        				usedBy{"container.zip/bin1"},
        				firstParty{},
        				hr{},
        				library{"Android"},
        				usedBy{"container.zip/bin2"},
        				library{"Device"},
        				usedBy{"container.zip/bin1"},
        				usedBy{"container.zip/liba.so"},
        				library{"External"},
        				usedBy{"container.zip/bin1"},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/bin/bin2.meta_lic",
        				"testdata/proprietary/container.zip.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        				"testdata/restricted/RESTRICTED_LICENSE",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"application"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"application"},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/application.meta_lic",
        				"testdata/proprietary/bin/bin3.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"Android"},
        				usedBy{"bin/bin1"},
        				firstParty{},
        				hr{},
        				library{"Device"},
        				usedBy{"bin/bin1"},
        				library{"External"},
        				usedBy{"bin/bin1"},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				hr{},
        				library{"External"},
        				usedBy{"lib/libd.so"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        			stdout := &bytes.Buffer{}
        			stderr := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        			}
        
        			var deps []string
        
        			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
        
        			err := textNotice(&ctx, rootFiles...)
        			if err != nil {
        				t.Fatalf("textnotice: error = %v, stderr = %v", err, stderr)
        				return
        			}
        			if stderr.Len() > 0 {
        				t.Errorf("textnotice: gotStderr = %v, want none", stderr)
        			}
        
        			t.Logf("got stdout: %s", stdout.String())
        
        			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
        
        			out := bufio.NewScanner(stdout)
        			lineno := 0
        			for out.Scan() {
        				line := out.Text()
        				if strings.TrimLeft(line, " ") == "" {
        					continue
        				}
        				if len(tt.expectedOut) <= lineno {
        					t.Errorf("unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        				} else if !tt.expectedOut[lineno].isMatch(line) {
        					t.Errorf("unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
        				}
        				lineno++
        			}
        			for ; lineno < len(tt.expectedOut); lineno++ {
        				t.Errorf("textnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
        			}
        
        			t.Logf("got deps: %q", deps)
        
        			t.Logf("want deps: %q", tt.expectedDeps)
        
        			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
        				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
        					strings.Join(w, "\n"), strings.Join(g, "\n"))
        			}
        		})
        	}
        }
        
        type matcher interface {
        	isMatch(line string) bool
        	String() string
        }
        
        type hr struct{}
        
        func (m hr) isMatch(line string) bool {
        	return horizontalRule.MatchString(line)
        }
        
        func (m hr) String() string {
        	return " ================================================== "
        }
        
        type library struct {
        	name string
        }
        
        func (m library) isMatch(line string) bool {
        	return strings.HasPrefix(line, m.name+" ")
        }
        
        func (m library) String() string {
        	return m.name + " used by:"
        }
        
        type usedBy struct {
        	name string
        }
        
        func (m usedBy) isMatch(line string) bool {
        	return len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, " "), "out/") && strings.HasSuffix(line, "/"+m.name)
        }
        
        func (m usedBy) String() string {
        	return "  out/.../" + m.name
        }
        
        type firstParty struct{}
        
        func (m firstParty) isMatch(line string) bool {
        	return strings.HasPrefix(strings.TrimLeft(line, " "), "&&&First Party License&&&")
        }
        
        func (m firstParty) String() string {
        	return "&&&First Party License&&&"
        }
        
        type notice struct{}
        
        func (m notice) isMatch(line string) bool {
        	return strings.HasPrefix(strings.TrimLeft(line, " "), "%%%Notice License%%%")
        }
        
        func (m notice) String() string {
        	return "%%%Notice License%%%"
        }
        
        type reciprocal struct{}
        
        func (m reciprocal) isMatch(line string) bool {
        	return strings.HasPrefix(strings.TrimLeft(line, " "), "$$$Reciprocal License$$$")
        }
        
        func (m reciprocal) String() string {
        	return "$$$Reciprocal License$$$"
        }
        
        type restricted struct{}
        
        func (m restricted) isMatch(line string) bool {
        	return strings.HasPrefix(strings.TrimLeft(line, " "), "###Restricted License###")
        }
        
        func (m restricted) String() string {
        	return "###Restricted License###"
        }
        
        type proprietary struct{}
        
        func (m proprietary) isMatch(line string) bool {
        	return strings.HasPrefix(strings.TrimLeft(line, " "), "@@@Proprietary License@@@")
        }
        
        func (m proprietary) String() string {
        	return "@@@Proprietary License@@@"
        }
        
        type matcherList []matcher
        
        func (l matcherList) String() string {
        	var sb strings.Builder
        	for _, m := range l {
        		s := m.String()
        		if s[:3] == s[len(s)-3:] {
        			fmt.Fprintln(&sb)
        		}
        		fmt.Fprintf(&sb, "%s\n", s)
        		if s[:3] == s[len(s)-3:] {
        			fmt.Fprintln(&sb)
        		}
        	}
        	return sb.String()
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/xmlnotice/xmlnotice.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bytes"
        	"compress/gzip"
        	"encoding/xml"
        	"flag"
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"path/filepath"
        	"sort"
        	"strings"
        
        	"android/soong/response"
        	"android/soong/tools/compliance"
        
        	"github.com/google/blueprint/deptools"
        )
        
        var (
        	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
        	failNoLicenses    = fmt.Errorf("No licenses found")
        )
        
        type context struct {
        	stdout      io.Writer
        	stderr      io.Writer
        	rootFS      fs.FS
        	product     string
        	stripPrefix []string
        	title       string
        	deps        *[]string
        }
        
        func (ctx context) strip(installPath string) string {
        	for _, prefix := range ctx.stripPrefix {
        		if strings.HasPrefix(installPath, prefix) {
        			p := strings.TrimPrefix(installPath, prefix)
        			if 0 == len(p) {
        				p = ctx.product
        			}
        			if 0 == len(p) {
        				continue
        			}
        			return p
        		}
        	}
        	return installPath
        }
        
        // newMultiString creates a flag that allows multiple values in an array.
        func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
        	var f multiString
        	flags.Var(&f, name, usage)
        	return &f
        }
        
        // multiString implements the flag `Value` interface for multiple strings.
        type multiString []string
        
        func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
        func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
        
        func main() {
        	var expandedArgs []string
        	for _, arg := range os.Args[1:] {
        		if strings.HasPrefix(arg, "@") {
        			f, err := os.Open(strings.TrimPrefix(arg, "@"))
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        
        			respArgs, err := response.ReadRspFile(f)
        			f.Close()
        			if err != nil {
        				fmt.Fprintln(os.Stderr, err.Error())
        				os.Exit(1)
        			}
        			expandedArgs = append(expandedArgs, respArgs...)
        		} else {
        			expandedArgs = append(expandedArgs, arg)
        		}
        	}
        
        	flags := flag.NewFlagSet("flags", flag.ExitOnError)
        
        	flags.Usage = func() {
        		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
        
        Outputs an xml NOTICE.xml or gzipped NOTICE.xml.gz file if the -o filename ends
        with ".gz".
        
        Options:
        `, filepath.Base(os.Args[0]))
        		flags.PrintDefaults()
        	}
        
        	outputFile := flags.String("o", "-", "Where to write the NOTICE xml or xml.gz file. (default stdout)")
        	depsFile := flags.String("d", "", "Where to write the deps file")
        	product := flags.String("product", "", "The name of the product for which the notice is generated.")
        	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
        	title := flags.String("title", "", "The title of the notice file.")
        
        	flags.Parse(expandedArgs)
        
        	// Must specify at least one root target.
        	if flags.NArg() == 0 {
        		flags.Usage()
        		os.Exit(2)
        	}
        
        	if len(*outputFile) == 0 {
        		flags.Usage()
        		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
        		os.Exit(2)
        	} else {
        		dir, err := filepath.Abs(filepath.Dir(*outputFile))
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        		fi, err := os.Stat(dir)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
        			os.Exit(1)
        		}
        		if !fi.IsDir() {
        			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
        			os.Exit(1)
        		}
        	}
        
        	var ofile io.Writer
        	var closer io.Closer
        	ofile = os.Stdout
        	var obuf *bytes.Buffer
        	if *outputFile != "-" {
        		obuf = &bytes.Buffer{}
        		ofile = obuf
        	}
        	if strings.HasSuffix(*outputFile, ".gz") {
        		ofile, _ = gzip.NewWriterLevel(obuf, gzip.BestCompression)
        		closer = ofile.(io.Closer)
        	}
        
        	var deps []string
        
        	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
        
        	err := xmlNotice(ctx, flags.Args()...)
        	if err != nil {
        		if err == failNoneRequested {
        			flags.Usage()
        		}
        		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
        		os.Exit(1)
        	}
        	if closer != nil {
        		closer.Close()
        	}
        
        	if *outputFile != "-" {
        		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
        			os.Exit(1)
        		}
        	}
        	if *depsFile != "" {
        		err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
        		if err != nil {
        			fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
        			os.Exit(1)
        		}
        	}
        	os.Exit(0)
        }
        
        // xmlNotice implements the xmlnotice utility.
        func xmlNotice(ctx *context, files ...string) error {
        	// Must be at least one root file.
        	if len(files) < 1 {
        		return failNoneRequested
        	}
        
        	// Read the license graph from the license metadata files (*.meta_lic).
        	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
        	if err != nil {
        		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
        	}
        	if licenseGraph == nil {
        		return failNoLicenses
        	}
        
        	// rs contains all notice resolutions.
        	rs := compliance.ResolveNotices(licenseGraph)
        
        	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
        	if err != nil {
        		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
        	}
        
        	fmt.Fprintln(ctx.stdout, "")
        	fmt.Fprintln(ctx.stdout, "")
        
        	for installPath := range ni.InstallPaths() {
        		p := ctx.strip(installPath)
        		for _, h := range ni.InstallHashes(installPath) {
        			for _, lib := range ni.InstallHashLibs(installPath, h) {
        				fmt.Fprintf(ctx.stdout, "")
        				xml.EscapeText(ctx.stdout, []byte(p))
        				fmt.Fprintln(ctx.stdout, "")
        			}
        		}
        	}
        	for h := range ni.Hashes() {
        		fmt.Fprintf(ctx.stdout, "\n\n")
        	}
        	fmt.Fprintln(ctx.stdout, "")
        
        	*ctx.deps = ni.InputFiles()
        	sort.Strings(*ctx.deps)
        
        	return nil
        }
        
        
        ================================================
        FILE: tools/compliance/cmd/xmlnotice/xmlnotice_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package main
        
        import (
        	"bufio"
        	"bytes"
        	"encoding/xml"
        	"fmt"
        	"os"
        	"reflect"
        	"regexp"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance"
        )
        
        var (
        	installTarget = regexp.MustCompile(`^([^<]+)`)
        	licenseText = regexp.MustCompile(`^`)
        )
        
        func TestMain(m *testing.M) {
        	// Change into the parent directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir(".."); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func Test(t *testing.T) {
        	tests := []struct {
        		condition    string
        		name         string
        		outDir       string
        		roots        []string
        		stripPrefix  string
        		expectedOut  []matcher
        		expectedDeps []string
        	}{
        		{
        			condition: "firstparty",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				target{"highest.apex", "Android"},
        				target{"highest.apex/bin/bin1", "Android"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/lib/liba.so", "Android"},
        				target{"highest.apex/lib/libb.so", "Android"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/bin/bin2.meta_lic",
        				"testdata/firstparty/highest.apex.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				target{"container.zip", "Android"},
        				target{"container.zip/bin1", "Android"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/liba.so", "Android"},
        				target{"container.zip/libb.so", "Android"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/bin/bin2.meta_lic",
        				"testdata/firstparty/container.zip.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				target{"application", "Android"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/application.meta_lic",
        				"testdata/firstparty/bin/bin3.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				target{"bin/bin1", "Android"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/bin/bin1.meta_lic",
        				"testdata/firstparty/lib/liba.so.meta_lic",
        				"testdata/firstparty/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "firstparty",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				target{"lib/libd.so", "Android"},
        				firstParty{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/firstparty/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				target{"highest.apex", "Android"},
        				target{"highest.apex/bin/bin1", "Android"},
        				target{"highest.apex/bin/bin1", "Device"},
        				target{"highest.apex/bin/bin1", "External"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/lib/liba.so", "Device"},
        				target{"highest.apex/lib/libb.so", "Android"},
        				firstParty{},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				target{"container.zip", "Android"},
        				target{"container.zip/bin1", "Android"},
        				target{"container.zip/bin1", "Device"},
        				target{"container.zip/bin1", "External"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/liba.so", "Device"},
        				target{"container.zip/libb.so", "Android"},
        				firstParty{},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				target{"application", "Android"},
        				target{"application", "Device"},
        				firstParty{},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				target{"bin/bin1", "Android"},
        				target{"bin/bin1", "Device"},
        				target{"bin/bin1", "External"},
        				firstParty{},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "notice",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				target{"lib/libd.so", "External"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				target{"highest.apex", "Android"},
        				target{"highest.apex/bin/bin1", "Android"},
        				target{"highest.apex/bin/bin1", "Device"},
        				target{"highest.apex/bin/bin1", "External"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/lib/liba.so", "Device"},
        				target{"highest.apex/lib/libb.so", "Android"},
        				firstParty{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/bin/bin2.meta_lic",
        				"testdata/reciprocal/highest.apex.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				target{"container.zip", "Android"},
        				target{"container.zip/bin1", "Android"},
        				target{"container.zip/bin1", "Device"},
        				target{"container.zip/bin1", "External"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/liba.so", "Device"},
        				target{"container.zip/libb.so", "Android"},
        				firstParty{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/bin/bin2.meta_lic",
        				"testdata/reciprocal/container.zip.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				target{"application", "Android"},
        				target{"application", "Device"},
        				firstParty{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/application.meta_lic",
        				"testdata/reciprocal/bin/bin3.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				target{"bin/bin1", "Android"},
        				target{"bin/bin1", "Device"},
        				target{"bin/bin1", "External"},
        				firstParty{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/reciprocal/bin/bin1.meta_lic",
        				"testdata/reciprocal/lib/liba.so.meta_lic",
        				"testdata/reciprocal/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "reciprocal",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				target{"lib/libd.so", "External"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/reciprocal/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				target{"highest.apex", "Android"},
        				target{"highest.apex/bin/bin1", "Android"},
        				target{"highest.apex/bin/bin1", "Device"},
        				target{"highest.apex/bin/bin1", "External"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/lib/liba.so", "Device"},
        				target{"highest.apex/lib/libb.so", "Android"},
        				firstParty{},
        				restricted{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/bin/bin2.meta_lic",
        				"testdata/restricted/highest.apex.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				target{"container.zip", "Android"},
        				target{"container.zip/bin1", "Android"},
        				target{"container.zip/bin1", "Device"},
        				target{"container.zip/bin1", "External"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/liba.so", "Device"},
        				target{"container.zip/libb.so", "Android"},
        				firstParty{},
        				restricted{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/bin/bin2.meta_lic",
        				"testdata/restricted/container.zip.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				target{"application", "Android"},
        				target{"application", "Device"},
        				firstParty{},
        				restricted{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/application.meta_lic",
        				"testdata/restricted/bin/bin3.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				target{"bin/bin1", "Android"},
        				target{"bin/bin1", "Device"},
        				target{"bin/bin1", "External"},
        				firstParty{},
        				restricted{},
        				reciprocal{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/reciprocal/RECIPROCAL_LICENSE",
        				"testdata/restricted/RESTRICTED_LICENSE",
        				"testdata/restricted/bin/bin1.meta_lic",
        				"testdata/restricted/lib/liba.so.meta_lic",
        				"testdata/restricted/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "restricted",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				target{"lib/libd.so", "External"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/restricted/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "apex",
        			roots:     []string{"highest.apex.meta_lic"},
        			expectedOut: []matcher{
        				target{"highest.apex", "Android"},
        				target{"highest.apex/bin/bin1", "Android"},
        				target{"highest.apex/bin/bin1", "Device"},
        				target{"highest.apex/bin/bin1", "External"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/bin/bin2", "Android"},
        				target{"highest.apex/lib/liba.so", "Device"},
        				target{"highest.apex/lib/libb.so", "Android"},
        				restricted{},
        				firstParty{},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/bin/bin2.meta_lic",
        				"testdata/proprietary/highest.apex.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        				"testdata/restricted/RESTRICTED_LICENSE",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "container",
        			roots:     []string{"container.zip.meta_lic"},
        			expectedOut: []matcher{
        				target{"container.zip", "Android"},
        				target{"container.zip/bin1", "Android"},
        				target{"container.zip/bin1", "Device"},
        				target{"container.zip/bin1", "External"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/bin2", "Android"},
        				target{"container.zip/liba.so", "Device"},
        				target{"container.zip/libb.so", "Android"},
        				restricted{},
        				firstParty{},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/bin/bin2.meta_lic",
        				"testdata/proprietary/container.zip.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        				"testdata/restricted/RESTRICTED_LICENSE",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "application",
        			roots:     []string{"application.meta_lic"},
        			expectedOut: []matcher{
        				target{"application", "Android"},
        				target{"application", "Device"},
        				firstParty{},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/application.meta_lic",
        				"testdata/proprietary/bin/bin3.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "binary",
        			roots:     []string{"bin/bin1.meta_lic"},
        			expectedOut: []matcher{
        				target{"bin/bin1", "Android"},
        				target{"bin/bin1", "Device"},
        				target{"bin/bin1", "External"},
        				firstParty{},
        				proprietary{},
        			},
        			expectedDeps: []string{
        				"testdata/firstparty/FIRST_PARTY_LICENSE",
        				"testdata/proprietary/PROPRIETARY_LICENSE",
        				"testdata/proprietary/bin/bin1.meta_lic",
        				"testdata/proprietary/lib/liba.so.meta_lic",
        				"testdata/proprietary/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			condition: "proprietary",
        			name:      "library",
        			roots:     []string{"lib/libd.so.meta_lic"},
        			expectedOut: []matcher{
        				target{"lib/libd.so", "External"},
        				notice{},
        			},
        			expectedDeps: []string{
        				"testdata/notice/NOTICE_LICENSE",
        				"testdata/proprietary/lib/libd.so.meta_lic",
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
        			stdout := &bytes.Buffer{}
        			stderr := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
        			}
        
        			var deps []string
        
        			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
        
        			err := xmlNotice(&ctx, rootFiles...)
        			if err != nil {
        				t.Fatalf("xmlnotice: error = %v, stderr = %v", err, stderr)
        				return
        			}
        			if stderr.Len() > 0 {
        				t.Errorf("xmlnotice: gotStderr = %v, want none", stderr)
        			}
        
        			t.Logf("got stdout: %s", stdout.String())
        
        			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
        
        			out := bufio.NewScanner(stdout)
        			lineno := 0
        			inBody := false
        			outOfBody := true
        			for out.Scan() {
        				line := out.Text()
        				if strings.TrimLeft(line, " ") == "" {
        					continue
        				}
        				if lineno == 0 && !inBody && `` == line {
        					continue
        				}
        				if !inBody {
        					if "" == line {
        						inBody = true
        						outOfBody = false
        					}
        					continue
        				} else if "" == line {
        					outOfBody = true
        					continue
        				}
        
        				if len(tt.expectedOut) <= lineno {
        					t.Errorf("xmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
        				} else if !tt.expectedOut[lineno].isMatch(line) {
        					t.Errorf("xmlnotice: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
        				}
        				lineno++
        			}
        			if !inBody {
        				t.Errorf("xmlnotice: missing  tag: got no  tag, want  tag on 2nd line")
        			}
        			if !outOfBody {
        				t.Errorf("xmlnotice: missing  tag: got no  tag, want  tag on last line")
        			}
        			for ; lineno < len(tt.expectedOut); lineno++ {
        				t.Errorf("xmlnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
        			}
        
        			t.Logf("got deps: %q", deps)
        
        			t.Logf("want deps: %q", tt.expectedDeps)
        
        			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
        				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
        					strings.Join(w, "\n"), strings.Join(g, "\n"))
        			}
        		})
        	}
        }
        
        func escape(s string) string {
        	b := &bytes.Buffer{}
        	xml.EscapeText(b, []byte(s))
        	return b.String()
        }
        
        type matcher interface {
        	isMatch(line string) bool
        	String() string
        }
        
        type target struct {
        	name string
        	lib string
        }
        
        func (m target) isMatch(line string) bool {
        	groups := installTarget.FindStringSubmatch(line)
        	if len(groups) != 3 {
        		return false
        	}
        	return groups[1] == escape(m.lib) && strings.HasPrefix(groups[2], "out/") && strings.HasSuffix(groups[2], "/"+escape(m.name))
        }
        
        func (m target) String() string {
        	return `` + escape(m.name) + ``
        }
        
        func matchesText(line, text string) bool {
        	groups := licenseText.FindStringSubmatch(line)
        	if len(groups) != 2 {
        		return false
        	}
        	return groups[1] == escape(text + "\n")
        }
        
        func expectedText(text string) string {
        	return ``
        }
        
        type firstParty struct{}
        
        func (m firstParty) isMatch(line string) bool {
        	return matchesText(line, "&&&First Party License&&&")
        }
        
        func (m firstParty) String() string {
        	return expectedText("&&&First Party License&&&")
        }
        
        type notice struct{}
        
        func (m notice) isMatch(line string) bool {
        	return matchesText(line, "%%%Notice License%%%")
        }
        
        func (m notice) String() string {
        	return expectedText("%%%Notice License%%%")
        }
        
        type reciprocal struct{}
        
        func (m reciprocal) isMatch(line string) bool {
        	return matchesText(line, "$$$Reciprocal License$$$")
        }
        
        func (m reciprocal) String() string {
        	return expectedText("$$$Reciprocal License$$$")
        }
        
        type restricted struct{}
        
        func (m restricted) isMatch(line string) bool {
        	return matchesText(line, "###Restricted License###")
        }
        
        func (m restricted) String() string {
        	return expectedText("###Restricted License###")
        }
        
        type proprietary struct{}
        
        func (m proprietary) isMatch(line string) bool {
        	return matchesText(line, "@@@Proprietary License@@@")
        }
        
        func (m proprietary) String() string {
        	return expectedText("@@@Proprietary License@@@")
        }
        
        type matcherList []matcher
        
        func (l matcherList) String() string {
        	var sb strings.Builder
        	fmt.Fprintln(&sb, ``)
        	fmt.Fprintln(&sb, ``)
        	for _, m := range l {
        		s := m.String()
        		fmt.Fprintln(&sb, s)
        		if _, ok := m.(target); !ok {
        			fmt.Fprintln(&sb)
        		}
        	}
        	fmt.Fprintln(&sb, `/`)
        	return sb.String()
        }
        
        
        ================================================
        FILE: tools/compliance/condition.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        )
        
        // LicenseCondition identifies a recognized license condition by setting the
        // corresponding bit.
        type LicenseCondition uint16
        
        // LicenseConditionMask is a bitmask for the recognized license conditions.
        const LicenseConditionMask = LicenseCondition(0x1ff)
        
        const (
        	// UnencumberedCondition identifies public domain or public domain-
        	// like license that disclaims copyright.
        	UnencumberedCondition = LicenseCondition(0x0001)
        	// PermissiveCondition identifies a license without notice or other
        	// significant requirements.
        	PermissiveCondition = LicenseCondition(0x0002)
        	// NoticeCondition identifies a typical open-source license with only
        	// notice or attribution requirements.
        	NoticeCondition = LicenseCondition(0x0004)
        	// ReciprocalCondition identifies a license with requirement to share
        	// the module's source only.
        	ReciprocalCondition = LicenseCondition(0x0008)
        	// RestrictedCondition identifies a license with requirement to share
        	// all source code linked to the module's source.
        	RestrictedCondition = LicenseCondition(0x0010)
        	// WeaklyRestrictedCondition identifies a RestrictedCondition waived
        	// for dynamic linking.
        	WeaklyRestrictedCondition = LicenseCondition(0x0020)
        	// ProprietaryCondition identifies a license with source privacy
        	// requirements.
        	ProprietaryCondition = LicenseCondition(0x0040)
        	// ByExceptionOnly identifies a license where policy requires product
        	// counsel review prior to use.
        	ByExceptionOnlyCondition = LicenseCondition(0x0080)
        	// NotAllowedCondition identifies a license with onerous conditions
        	// where policy prohibits use.
        	NotAllowedCondition = LicenseCondition(0x0100)
        )
        
        var (
        	// RecognizedConditionNames maps condition strings to LicenseCondition.
        	RecognizedConditionNames = map[string]LicenseCondition{
        		"unencumbered":                    UnencumberedCondition,
        		"permissive":                      PermissiveCondition,
        		"notice":                          NoticeCondition,
        		"reciprocal":                      ReciprocalCondition,
        		"restricted":                      RestrictedCondition,
        		"restricted_if_statically_linked": WeaklyRestrictedCondition,
        		"proprietary":                     ProprietaryCondition,
        		"by_exception_only":               ByExceptionOnlyCondition,
        		"not_allowed":                     NotAllowedCondition,
        	}
        )
        
        // Name returns the condition string corresponding to the LicenseCondition.
        func (lc LicenseCondition) Name() string {
        	switch lc {
        	case UnencumberedCondition:
        		return "unencumbered"
        	case PermissiveCondition:
        		return "permissive"
        	case NoticeCondition:
        		return "notice"
        	case ReciprocalCondition:
        		return "reciprocal"
        	case RestrictedCondition:
        		return "restricted"
        	case WeaklyRestrictedCondition:
        		return "restricted_if_statically_linked"
        	case ProprietaryCondition:
        		return "proprietary"
        	case ByExceptionOnlyCondition:
        		return "by_exception_only"
        	case NotAllowedCondition:
        		return "not_allowed"
        	}
        	panic(fmt.Errorf("unrecognized license condition: %#v", lc))
        }
        
        
        ================================================
        FILE: tools/compliance/condition_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"testing"
        )
        
        func TestConditionSetHas(t *testing.T) {
        	impliesShare := ImpliesShared
        
        	t.Logf("testing with imliesShare=%#v", impliesShare)
        
        	if impliesShare.HasAny(NoticeCondition) {
        		t.Errorf("impliesShare.HasAny(\"notice\"=%#v) got true, want false", NoticeCondition)
        	}
        
        	if !impliesShare.HasAny(RestrictedCondition) {
        		t.Errorf("impliesShare.HasAny(\"restricted\"=%#v) got false, want true", RestrictedCondition)
        	}
        
        	if !impliesShare.HasAny(ReciprocalCondition) {
        		t.Errorf("impliesShare.HasAny(\"reciprocal\"=%#v) got false, want true", ReciprocalCondition)
        	}
        
        	if impliesShare.HasAny(LicenseCondition(0x0000)) {
        		t.Errorf("impliesShare.HasAny(nil=%#v) got true, want false", LicenseCondition(0x0000))
        	}
        }
        
        func TestConditionName(t *testing.T) {
        	for expected, condition := range RecognizedConditionNames {
        		actual := condition.Name()
        		if expected != actual {
        			t.Errorf("unexpected name for condition %#v: got %s, want %s", condition, actual, expected)
        		}
        	}
        }
        
        func TestConditionName_InvalidCondition(t *testing.T) {
        	panicked := false
        	var lc LicenseCondition
        	func() {
        		defer func() {
        			if err := recover(); err != nil {
        				panicked = true
        			}
        		}()
        		name := lc.Name()
        		t.Errorf("invalid condition unexpected name: got %s, wanted panic", name)
        	}()
        	if !panicked {
        		t.Errorf("no expected panic for %#v.Name(): got no panic, wanted panic", lc)
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/conditionset.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"strings"
        )
        
        // LicenseConditionSet identifies sets of license conditions.
        type LicenseConditionSet LicenseCondition
        
        // AllLicenseConditions is the set of all recognized license conditions.
        const AllLicenseConditions = LicenseConditionSet(LicenseConditionMask)
        
        // NewLicenseConditionSet returns a set containing exactly the elements of
        // `conditions`.
        func NewLicenseConditionSet(conditions ...LicenseCondition) LicenseConditionSet {
        	cs := LicenseConditionSet(0x00)
        	for _, lc := range conditions {
        		cs |= LicenseConditionSet(lc)
        	}
        	return cs
        }
        
        // Plus returns a new set containing all of the elements of `cs` and all of the
        // `conditions`.
        func (cs LicenseConditionSet) Plus(conditions ...LicenseCondition) LicenseConditionSet {
        	result := cs
        	for _, lc := range conditions {
        		result |= LicenseConditionSet(lc)
        	}
        	return result
        }
        
        // Union returns a new set containing all of the elements of `cs` and all of the
        // elements of the `other` sets.
        func (cs LicenseConditionSet) Union(other ...LicenseConditionSet) LicenseConditionSet {
        	result := cs
        	for _, ls := range other {
        		result |= ls
        	}
        	return result
        }
        
        // MatchingAny returns the subset of `cs` equal to any of the `conditions`.
        func (cs LicenseConditionSet) MatchingAny(conditions ...LicenseCondition) LicenseConditionSet {
        	result := LicenseConditionSet(0x00)
        	for _, lc := range conditions {
        		result |= cs & LicenseConditionSet(lc)
        	}
        	return result
        }
        
        // MatchingAnySet returns the subset of `cs` that are members of any of the
        // `other` sets.
        func (cs LicenseConditionSet) MatchingAnySet(other ...LicenseConditionSet) LicenseConditionSet {
        	result := LicenseConditionSet(0x00)
        	for _, ls := range other {
        		result |= cs & ls
        	}
        	return result
        }
        
        // HasAny returns true when `cs` contains at least one of the `conditions`.
        func (cs LicenseConditionSet) HasAny(conditions ...LicenseCondition) bool {
        	for _, lc := range conditions {
        		if 0x0000 != (cs & LicenseConditionSet(lc)) {
        			return true
        		}
        	}
        	return false
        }
        
        // MatchesAnySet returns true when `cs` has a non-empty intersection with at
        // least one of the `other` condition sets.
        func (cs LicenseConditionSet) MatchesAnySet(other ...LicenseConditionSet) bool {
        	for _, ls := range other {
        		if 0x0000 != (cs & ls) {
        			return true
        		}
        	}
        	return false
        }
        
        // HasAll returns true when `cs` contains every one of the `conditions`.
        func (cs LicenseConditionSet) HasAll(conditions ...LicenseCondition) bool {
        	for _, lc := range conditions {
        		if 0x0000 == (cs & LicenseConditionSet(lc)) {
        			return false
        		}
        	}
        	return true
        }
        
        // MatchesEverySet returns true when `cs` has a non-empty intersection with
        // each of the `other` condition sets.
        func (cs LicenseConditionSet) MatchesEverySet(other ...LicenseConditionSet) bool {
        	for _, ls := range other {
        		if 0x0000 == (cs & ls) {
        			return false
        		}
        	}
        	return true
        }
        
        // Intersection returns the subset of `cs` that are members of every `other`
        // set.
        func (cs LicenseConditionSet) Intersection(other ...LicenseConditionSet) LicenseConditionSet {
        	result := cs
        	for _, ls := range other {
        		result &= ls
        	}
        	return result
        }
        
        // Minus returns the subset of `cs` that are not equaal to any `conditions`.
        func (cs LicenseConditionSet) Minus(conditions ...LicenseCondition) LicenseConditionSet {
        	result := cs
        	for _, lc := range conditions {
        		result &^= LicenseConditionSet(lc)
        	}
        	return result
        }
        
        // Difference returns the subset of `cs` that are not members of any `other`
        // set.
        func (cs LicenseConditionSet) Difference(other ...LicenseConditionSet) LicenseConditionSet {
        	result := cs
        	for _, ls := range other {
        		result &^= ls
        	}
        	return result
        }
        
        // Len returns the number of license conditions in the set.
        func (cs LicenseConditionSet) Len() int {
        	size := 0
        	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        		if 0x00 != (cs & lc) {
        			size++
        		}
        	}
        	return size
        }
        
        // AsList returns an array of the license conditions in the set.
        func (cs LicenseConditionSet) AsList() []LicenseCondition {
        	result := make([]LicenseCondition, 0, cs.Len())
        	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        		if 0x00 != (cs & lc) {
        			result = append(result, LicenseCondition(lc))
        		}
        	}
        	return result
        }
        
        // Names returns an array of the names of the license conditions in the set.
        func (cs LicenseConditionSet) Names() []string {
        	result := make([]string, 0, cs.Len())
        	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
        		if 0x00 != (cs & lc) {
        			result = append(result, LicenseCondition(lc).Name())
        		}
        	}
        	return result
        }
        
        // IsEmpty returns true when the set contains no license conditions.
        func (cs LicenseConditionSet) IsEmpty() bool {
        	return 0x00 == (cs & AllLicenseConditions)
        }
        
        // String returns a human-readable string representation of the set.
        func (cs LicenseConditionSet) String() string {
        	return fmt.Sprintf("{%s}", strings.Join(cs.Names(), "|"))
        }
        
        
        ================================================
        FILE: tools/compliance/conditionset_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"strings"
        	"testing"
        )
        
        func TestConditionSet(t *testing.T) {
        	tests := []struct {
        		name        string
        		conditions  []string
        		plus        *[]string
        		minus       *[]string
        		matchingAny map[string][]string
        		expected    []string
        	}{
        		{
        			name:       "empty",
        			conditions: []string{},
        			plus:       &[]string{},
        			matchingAny: map[string][]string{
        				"notice":                []string{},
        				"restricted":            []string{},
        				"restricted|reciprocal": []string{},
        			},
        			expected: []string{},
        		},
        		{
        			name:       "emptyminusnothing",
        			conditions: []string{},
        			minus:      &[]string{},
        			matchingAny: map[string][]string{
        				"notice":                []string{},
        				"restricted":            []string{},
        				"restricted|reciprocal": []string{},
        			},
        			expected: []string{},
        		},
        		{
        			name:       "emptyminusnotice",
        			conditions: []string{},
        			minus:      &[]string{"notice"},
        			matchingAny: map[string][]string{
        				"notice":                []string{},
        				"restricted":            []string{},
        				"restricted|reciprocal": []string{},
        			},
        			expected: []string{},
        		},
        		{
        			name:       "noticeonly",
        			conditions: []string{"notice"},
        			matchingAny: map[string][]string{
        				"notice":             []string{"notice"},
        				"notice|proprietary": []string{"notice"},
        				"restricted":         []string{},
        			},
        			expected: []string{"notice"},
        		},
        		{
        			name:       "allnoticeonly",
        			conditions: []string{"notice"},
        			plus:       &[]string{"notice"},
        			matchingAny: map[string][]string{
        				"notice":             []string{"notice"},
        				"notice|proprietary": []string{"notice"},
        				"restricted":         []string{},
        			},
        			expected: []string{"notice"},
        		},
        		{
        			name:       "emptyplusnotice",
        			conditions: []string{},
        			plus:       &[]string{"notice"},
        			matchingAny: map[string][]string{
        				"notice":             []string{"notice"},
        				"notice|proprietary": []string{"notice"},
        				"restricted":         []string{},
        			},
        			expected: []string{"notice"},
        		},
        		{
        			name:       "everything",
        			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
        			plus:       &[]string{"restricted_if_statically_linked", "by_exception_only", "not_allowed"},
        			matchingAny: map[string][]string{
        				"unencumbered":                    []string{"unencumbered"},
        				"permissive":                      []string{"permissive"},
        				"notice":                          []string{"notice"},
        				"reciprocal":                      []string{"reciprocal"},
        				"restricted":                      []string{"restricted"},
        				"restricted_if_statically_linked": []string{"restricted_if_statically_linked"},
        				"proprietary":                     []string{"proprietary"},
        				"by_exception_only":               []string{"by_exception_only"},
        				"not_allowed":                     []string{"not_allowed"},
        				"notice|proprietary":              []string{"notice", "proprietary"},
        			},
        			expected: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        		},
        		{
        			name: "everythingplusminusnothing",
        			conditions: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        			plus:  &[]string{},
        			minus: &[]string{},
        			matchingAny: map[string][]string{
        				"unencumbered|permissive|notice": []string{"unencumbered", "permissive", "notice"},
        				"restricted|reciprocal":          []string{"reciprocal", "restricted"},
        				"proprietary|by_exception_only":  []string{"proprietary", "by_exception_only"},
        				"not_allowed":                    []string{"not_allowed"},
        			},
        			expected: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        		},
        		{
        			name:       "allbutone",
        			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
        			plus:       &[]string{"restricted_if_statically_linked", "by_exception_only", "not_allowed"},
        			matchingAny: map[string][]string{
        				"unencumbered":                    []string{"unencumbered"},
        				"permissive":                      []string{"permissive"},
        				"notice":                          []string{"notice"},
        				"reciprocal":                      []string{"reciprocal"},
        				"restricted":                      []string{"restricted"},
        				"restricted_if_statically_linked": []string{"restricted_if_statically_linked"},
        				"proprietary":                     []string{"proprietary"},
        				"by_exception_only":               []string{"by_exception_only"},
        				"not_allowed":                     []string{"not_allowed"},
        				"notice|proprietary":              []string{"notice", "proprietary"},
        			},
        			expected: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        		},
        		{
        			name: "everythingminusone",
        			conditions: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        			minus: &[]string{"restricted_if_statically_linked"},
        			matchingAny: map[string][]string{
        				"unencumbered":                    []string{"unencumbered"},
        				"permissive":                      []string{"permissive"},
        				"notice":                          []string{"notice"},
        				"reciprocal":                      []string{"reciprocal"},
        				"restricted":                      []string{"restricted"},
        				"restricted_if_statically_linked": []string{},
        				"proprietary":                     []string{"proprietary"},
        				"by_exception_only":               []string{"by_exception_only"},
        				"not_allowed":                     []string{"not_allowed"},
        				"restricted|proprietary":          []string{"restricted", "proprietary"},
        			},
        			expected: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        		},
        		{
        			name: "everythingminuseverything",
        			conditions: []string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        			minus: &[]string{
        				"unencumbered",
        				"permissive",
        				"notice",
        				"reciprocal",
        				"restricted",
        				"restricted_if_statically_linked",
        				"proprietary",
        				"by_exception_only",
        				"not_allowed",
        			},
        			matchingAny: map[string][]string{
        				"unencumbered":                    []string{},
        				"permissive":                      []string{},
        				"notice":                          []string{},
        				"reciprocal":                      []string{},
        				"restricted":                      []string{},
        				"restricted_if_statically_linked": []string{},
        				"proprietary":                     []string{},
        				"by_exception_only":               []string{},
        				"not_allowed":                     []string{},
        				"restricted|proprietary":          []string{},
        			},
        			expected: []string{},
        		},
        		{
        			name:       "restrictedplus",
        			conditions: []string{"restricted", "restricted_if_statically_linked"},
        			plus:       &[]string{"permissive", "notice", "restricted", "proprietary"},
        			matchingAny: map[string][]string{
        				"unencumbered":                    []string{},
        				"permissive":                      []string{"permissive"},
        				"notice":                          []string{"notice"},
        				"restricted":                      []string{"restricted"},
        				"restricted_if_statically_linked": []string{"restricted_if_statically_linked"},
        				"proprietary":                     []string{"proprietary"},
        				"restricted|proprietary":          []string{"restricted", "proprietary"},
        				"by_exception_only":               []string{},
        				"proprietary|by_exception_only":   []string{"proprietary"},
        			},
        			expected: []string{"permissive", "notice", "restricted", "restricted_if_statically_linked", "proprietary"},
        		},
        	}
        	for _, tt := range tests {
        		toConditions := func(names []string) []LicenseCondition {
        			result := make([]LicenseCondition, 0, len(names))
        			for _, name := range names {
        				result = append(result, RecognizedConditionNames[name])
        			}
        			return result
        		}
        		populate := func() LicenseConditionSet {
        			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        			if tt.plus != nil {
        				testSet = testSet.Plus(toConditions(*tt.plus)...)
        			}
        			if tt.minus != nil {
        				testSet = testSet.Minus(toConditions(*tt.minus)...)
        			}
        			return testSet
        		}
        		populateSet := func() LicenseConditionSet {
        			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        			if tt.plus != nil {
        				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
        			}
        			if tt.minus != nil {
        				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
        			}
        			return testSet
        		}
        		populatePlusSet := func() LicenseConditionSet {
        			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        			if tt.plus != nil {
        				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
        			}
        			if tt.minus != nil {
        				testSet = testSet.Minus(toConditions(*tt.minus)...)
        			}
        			return testSet
        		}
        		populateMinusSet := func() LicenseConditionSet {
        			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
        			if tt.plus != nil {
        				testSet = testSet.Plus(toConditions(*tt.plus)...)
        			}
        			if tt.minus != nil {
        				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
        			}
        			return testSet
        		}
        		checkMatching := func(cs LicenseConditionSet, t *testing.T) {
        			for data, expectedNames := range tt.matchingAny {
        				expectedConditions := toConditions(expectedNames)
        				expected := NewLicenseConditionSet(expectedConditions...)
        				actual := cs.MatchingAny(toConditions(strings.Split(data, "|"))...)
        				actualNames := actual.Names()
        
        				t.Logf("MatchingAny(%s): actual set %#v %s", data, actual, actual.String())
        				t.Logf("MatchingAny(%s): expected set %#v %s", data, expected, expected.String())
        
        				if actual != expected {
        					t.Errorf("MatchingAny(%s): got %#v, want %#v", data, actual, expected)
        					continue
        				}
        				if len(actualNames) != len(expectedNames) {
        					t.Errorf("len(MatchinAny(%s).Names()): got %d, want %d",
        						data, len(actualNames), len(expectedNames))
        				} else {
        					for i := 0; i < len(actualNames); i++ {
        						if actualNames[i] != expectedNames[i] {
        							t.Errorf("MatchingAny(%s).Names()[%d]: got %s, want %s",
        								data, i, actualNames[i], expectedNames[i])
        							break
        						}
        					}
        				}
        				actualConditions := actual.AsList()
        				if len(actualConditions) != len(expectedConditions) {
        					t.Errorf("len(MatchingAny(%s).AsList()):  got %d, want %d",
        						data, len(actualNames), len(expectedNames))
        				} else {
        					for i := 0; i < len(actualNames); i++ {
        						if actualNames[i] != expectedNames[i] {
        							t.Errorf("MatchingAny(%s).AsList()[%d]: got %s, want %s",
        								data, i, actualNames[i], expectedNames[i])
        							break
        						}
        					}
        				}
        			}
        		}
        		checkMatchingSet := func(cs LicenseConditionSet, t *testing.T) {
        			for data, expectedNames := range tt.matchingAny {
        				expected := NewLicenseConditionSet(toConditions(expectedNames)...)
        				actual := cs.MatchingAnySet(NewLicenseConditionSet(toConditions(strings.Split(data, "|"))...))
        				actualNames := actual.Names()
        
        				t.Logf("MatchingAnySet(%s): actual set %#v %s", data, actual, actual.String())
        				t.Logf("MatchingAnySet(%s): expected set %#v %s", data, expected, expected.String())
        
        				if actual != expected {
        					t.Errorf("MatchingAnySet(%s): got %#v, want %#v", data, actual, expected)
        					continue
        				}
        				if len(actualNames) != len(expectedNames) {
        					t.Errorf("len(MatchingAnySet(%s).Names()): got %d, want %d",
        						data, len(actualNames), len(expectedNames))
        				} else {
        					for i := 0; i < len(actualNames); i++ {
        						if actualNames[i] != expectedNames[i] {
        							t.Errorf("MatchingAnySet(%s).Names()[%d]: got %s, want %s",
        								data, i, actualNames[i], expectedNames[i])
        							break
        						}
        					}
        				}
        				expectedConditions := toConditions(expectedNames)
        				actualConditions := actual.AsList()
        				if len(actualConditions) != len(expectedConditions) {
        					t.Errorf("len(MatchingAnySet(%s).AsList()): got %d, want %d",
        						data, len(actualNames), len(expectedNames))
        				} else {
        					for i := 0; i < len(actualNames); i++ {
        						if actualNames[i] != expectedNames[i] {
        							t.Errorf("MatchingAnySet(%s).AsList()[%d]: got %s, want %s",
        								data, i, actualNames[i], expectedNames[i])
        							break
        						}
        					}
        				}
        			}
        		}
        
        		checkExpected := func(actual LicenseConditionSet, t *testing.T) bool {
        			t.Logf("checkExpected{%s}", strings.Join(tt.expected, ", "))
        
        			expectedConditions := toConditions(tt.expected)
        			expected := NewLicenseConditionSet(expectedConditions...)
        
        			actualNames := actual.Names()
        
        			t.Logf("actual license condition set: %#v %s", actual, actual.String())
        			t.Logf("expected license condition set: %#v %s", expected, expected.String())
        
        			if actual != expected {
        				t.Errorf("checkExpected: got %#v, want %#v", actual, expected)
        				return false
        			}
        
        			if len(actualNames) != len(tt.expected) {
        				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
        			} else {
        				for i := 0; i < len(actualNames); i++ {
        					if actualNames[i] != tt.expected[i] {
        						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
        						break
        					}
        				}
        			}
        
        			actualConditions := actual.AsList()
        			if len(actualConditions) != len(expectedConditions) {
        				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
        			} else {
        				for i := 0; i < len(actualConditions); i++ {
        					if actualConditions[i] != expectedConditions[i] {
        						t.Errorf("actual.AsList()[%d]: got %s, want %s",
        							i, actualConditions[i].Name(), expectedConditions[i].Name())
        						break
        					}
        				}
        			}
        
        			if len(tt.expected) == 0 {
        				if !actual.IsEmpty() {
        					t.Errorf("actual.IsEmpty(): got false, want true")
        				}
        				if actual.HasAny(expectedConditions...) {
        					t.Errorf("actual.HasAny(): got true, want false")
        				}
        			} else {
        				if actual.IsEmpty() {
        					t.Errorf("actual.IsEmpty(): got true, want false")
        				}
        				if !actual.HasAny(expectedConditions...) {
        					t.Errorf("actual.HasAny(all expected): got false, want true")
        				}
        			}
        			if !actual.HasAll(expectedConditions...) {
        				t.Errorf("actual.Hasll(all expected): want true, got false")
        			}
        			for _, expectedCondition := range expectedConditions {
        				if !actual.HasAny(expectedCondition) {
        					t.Errorf("actual.HasAny(%q): got false, want true", expectedCondition.Name())
        				}
        				if !actual.HasAll(expectedCondition) {
        					t.Errorf("actual.HasAll(%q): got false, want true", expectedCondition.Name())
        				}
        			}
        
        			notExpected := (AllLicenseConditions &^ expected)
        			notExpectedList := notExpected.AsList()
        			t.Logf("not expected license condition set: %#v %s", notExpected, notExpected.String())
        
        			if len(tt.expected) == 0 {
        				if actual.HasAny(append(expectedConditions, notExpectedList...)...) {
        					t.Errorf("actual.HasAny(all conditions): want false, got true")
        				}
        			} else {
        				if !actual.HasAny(append(expectedConditions, notExpectedList...)...) {
        					t.Errorf("actual.HasAny(all conditions): want true, got false")
        				}
        			}
        			if len(notExpectedList) == 0 {
        				if !actual.HasAll(append(expectedConditions, notExpectedList...)...) {
        					t.Errorf("actual.HasAll(all conditions): want true, got false")
        				}
        			} else {
        				if actual.HasAll(append(expectedConditions, notExpectedList...)...) {
        					t.Errorf("actual.HasAll(all conditions): want false, got true")
        				}
        			}
        			for _, unexpectedCondition := range notExpectedList {
        				if actual.HasAny(unexpectedCondition) {
        					t.Errorf("actual.HasAny(%q): got true, want false", unexpectedCondition.Name())
        				}
        				if actual.HasAll(unexpectedCondition) {
        					t.Errorf("actual.HasAll(%q): got true, want false", unexpectedCondition.Name())
        				}
        			}
        			return true
        		}
        
        		checkExpectedSet := func(actual LicenseConditionSet, t *testing.T) bool {
        			t.Logf("checkExpectedSet{%s}", strings.Join(tt.expected, ", "))
        
        			expectedConditions := toConditions(tt.expected)
        			expected := NewLicenseConditionSet(expectedConditions...)
        
        			actualNames := actual.Names()
        
        			t.Logf("actual license condition set: %#v %s", actual, actual.String())
        			t.Logf("expected license condition set: %#v %s", expected, expected.String())
        
        			if actual != expected {
        				t.Errorf("checkExpectedSet: got %#v, want %#v", actual, expected)
        				return false
        			}
        
        			if len(actualNames) != len(tt.expected) {
        				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
        			} else {
        				for i := 0; i < len(actualNames); i++ {
        					if actualNames[i] != tt.expected[i] {
        						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
        						break
        					}
        				}
        			}
        
        			actualConditions := actual.AsList()
        			if len(actualConditions) != len(expectedConditions) {
        				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
        			} else {
        				for i := 0; i < len(actualConditions); i++ {
        					if actualConditions[i] != expectedConditions[i] {
        						t.Errorf("actual.AsList()[%d}: got %s, want %s",
        							i, actualConditions[i].Name(), expectedConditions[i].Name())
        						break
        					}
        				}
        			}
        
        			if len(tt.expected) == 0 {
        				if !actual.IsEmpty() {
        					t.Errorf("actual.IsEmpty(): got false, want true")
        				}
        				if actual.MatchesAnySet(expected) {
        					t.Errorf("actual.MatchesAnySet({}): got true, want false")
        				}
        				if actual.MatchesEverySet(expected, expected) {
        					t.Errorf("actual.MatchesEverySet({}, {}): want false, got true")
        				}
        			} else {
        				if actual.IsEmpty() {
        					t.Errorf("actual.IsEmpty(): got true, want false")
        				}
        				if !actual.MatchesAnySet(expected) {
        					t.Errorf("actual.MatchesAnySet({all expected}): want true, got false")
        				}
        				if !actual.MatchesEverySet(expected, expected) {
        					t.Errorf("actual.MatchesEverySet({all expected}, {all expected}): want true, got false")
        				}
        			}
        
        			notExpected := (AllLicenseConditions &^ expected)
        			t.Logf("not expected license condition set: %#v %s", notExpected, notExpected.String())
        
        			if len(tt.expected) == 0 {
        				if actual.MatchesAnySet(expected, notExpected) {
        					t.Errorf("empty actual.MatchesAnySet({expected}, {not expected}): want false, got true")
        				}
        			} else {
        				if !actual.MatchesAnySet(expected, notExpected) {
        					t.Errorf("actual.MatchesAnySet({expected}, {not expected}): want true, got false")
        				}
        			}
        			if actual.MatchesAnySet(notExpected) {
        				t.Errorf("actual.MatchesAnySet({not expected}): want false, got true")
        			}
        			if actual.MatchesEverySet(notExpected) {
        				t.Errorf("actual.MatchesEverySet({not expected}): want false, got true")
        			}
        			if actual.MatchesEverySet(expected, notExpected) {
        				t.Errorf("actual.MatchesEverySet({expected}, {not expected}): want false, got true")
        			}
        
        			if !actual.Difference(expected).IsEmpty() {
        				t.Errorf("actual.Difference({expected}).IsEmpty(): want true, got false")
        			}
        			if expected != actual.Intersection(expected) {
        				t.Errorf("expected == actual.Intersection({expected}): want true, got false (%#v != %#v)", expected, actual.Intersection(expected))
        			}
        			if actual != actual.Intersection(expected) {
        				t.Errorf("actual == actual.Intersection({expected}): want true, got false (%#v != %#v)", actual, actual.Intersection(expected))
        			}
        			return true
        		}
        
        		t.Run(tt.name, func(t *testing.T) {
        			cs := populate()
        			if checkExpected(cs, t) {
        				checkMatching(cs, t)
        			}
        			if checkExpectedSet(cs, t) {
        				checkMatchingSet(cs, t)
        			}
        		})
        
        		t.Run(tt.name+"_sets", func(t *testing.T) {
        			cs := populateSet()
        			if checkExpected(cs, t) {
        				checkMatching(cs, t)
        			}
        			if checkExpectedSet(cs, t) {
        				checkMatchingSet(cs, t)
        			}
        		})
        
        		t.Run(tt.name+"_plusset", func(t *testing.T) {
        			cs := populatePlusSet()
        			if checkExpected(cs, t) {
        				checkMatching(cs, t)
        			}
        			if checkExpectedSet(cs, t) {
        				checkMatchingSet(cs, t)
        			}
        		})
        
        		t.Run(tt.name+"_minusset", func(t *testing.T) {
        			cs := populateMinusSet()
        			if checkExpected(cs, t) {
        				checkMatching(cs, t)
        			}
        			if checkExpectedSet(cs, t) {
        				checkMatchingSet(cs, t)
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/doc.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        // Much of this content appears too in README.md
        // When changing this file consider whether the change also applies to README.md
        
        /*
        
        Package compliance provides an approved means for reading, consuming, and
        analyzing license metadata graphs.
        
        Assuming the license metadata and dependencies are fully and accurately
        recorded in the build system, any discrepancy between the official policy for
        open source license compliance and this code is a bug in this code.
        
        A few principal types to understand are LicenseGraph, LicenseCondition, and
        ResolutionSet.
        
        LicenseGraph
        ------------
        
        A LicenseGraph is an immutable graph of the targets and dependencies reachable
        from a specific set of root targets. In general, the root targets will be the
        artifacts in a release or distribution. While conceptually immutable, parts of
        the graph may be loaded or evaluated lazily.
        
        Conceptually, the graph itself will always be a directed acyclic graph. One
        representation is a set of directed edges. Another is a set of nodes with
        directed edges to their dependencies.
        
        The edges have annotations, which can distinguish between build tools, runtime
        dependencies, and dependencies like 'contains' that make a derivative work.
        
        LicenseCondition
        ----------------
        
        A LicenseCondition is an immutable tuple pairing a condition name with an
        originating target. e.g. Per current policy, a static library licensed under an
        MIT license would pair a "notice" condition with the static library target, and
        a dynamic license licensed under GPL would pair a "restricted" condition with
        the dynamic library target.
        
        ResolutionSet
        -------------
        
        A ResolutionSet is an immutable set of `AttachesTo`, `ActsOn`, `Resolves`
        tuples describing how license conditions apply to targets.
        
        `AttachesTo` is the trigger for acting. Distribution of the target invokes
        the policy.
        
        `ActsOn` is the target to share, give notice for, hide etc.
        
        `Resolves` is the set of condition types that the action resolves.
        
        For most condition types, `ActsOn` will be the target where the condition
        originated. For example, a notice condition policy means attribution or notice
        must be given for the target where the condition originates. Likewise, a
        proprietary condition policy means the privacy of the target where the
        condition originates must be respected. i.e. The thing acted on is the origin.
        
        Restricted conditions are different. The infectious nature of restricted often
        means sharing code that is not the target where the restricted condition
        originates. Linking an MIT library to a GPL library implies a policy to share
        the MIT library despite the MIT license having no source sharing requirement.
        
        In this case, one or more resolution tuples will have the MIT license module in
        `ActsOn` and the restricted condition originating at the GPL library module in
        `Resolves`. These tuples will `AttachTo` every target that depends on the GPL
        library because shipping any of those targets trigger the policy to share the
        code.
        */
        package compliance
        
        
        ================================================
        FILE: tools/compliance/go.mod
        ================================================
        go 1.22
        
        module android/soong/tools/compliance
        
        require (
        	github.com/google/blueprint v0.0.0
        	android/soong v0.0.0
        	google.golang.org/protobuf v0.0.0
        	github.com/spdx/tools-golang v0.0.0
        	github.com/google/go-cmp v0.0.0
        )
        
        
        ================================================
        FILE: tools/compliance/go.work
        ================================================
        go 1.23
        
        use (
        	.
        	../../../../build/blueprint
        	../../../../build/soong
        	../../../../external/go-cmp
        	../../../../external/golang-protobuf
        	../../../../external/spdx-tools
        )
        
        replace (
        	github.com/google/blueprint v0.0.0 => ../../../../build/blueprint
        	android/soong v0.0.0 => ../../../../build/soong
        	github.com/google/go-cmp v0.0.0 => ../../../../external/go-cmp
        	google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
        	github.com/spdx/tools-golang v0.0.0 => ../../../../external/spdx-tools
        )
        
        
        ================================================
        FILE: tools/compliance/graph.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"sort"
        	"strings"
        	"sync"
        )
        
        // LicenseGraph describes the immutable license metadata for a set of root
        // targets and the transitive closure of their dependencies.
        //
        // Alternatively, a graph is a set of edges. In this case directed, annotated
        // edges from targets to dependencies.
        //
        // A LicenseGraph provides the frame of reference for all of the other types
        // defined here. It is possible to have multiple graphs, and to have targets,
        // edges, and resolutions from multiple graphs. But it is an error to try to
        // mix items from different graphs in the same operation.
        // May panic if attempted.
        //
        // The compliance package assumes specific private implementations of each of
        // these interfaces. May panic if attempts are made to combine different
        // implementations of some interfaces with expected implementations of other
        // interfaces here.
        type LicenseGraph struct {
        	// rootFiles identifies the original set of files to read. (immutable)
        	//
        	// Defines the starting "top" for top-down walks.
        	//
        	// Alternatively, an instance of licenseGraphImp conceptually defines a scope within
        	// the universe of build graphs as a sub-graph rooted at rootFiles where all edges
        	// and targets for the instance are defined relative to and within that scope. For
        	// most analyses, the correct scope is to root the graph at all of the distributed
        	// artifacts.
        	rootFiles []string
        
        	// edges lists the directed edges in the graph from target to dependency. (guarded by mu)
        	//
        	// Alternatively, the graph is the set of `edges`.
        	edges TargetEdgeList
        
        	// targets identifies, indexes, and describes the entire set of target node files.
        	/// (guarded by mu)
        	targets map[string]*TargetNode
        
        	// onceBottomUp makes sure the bottom-up resolve walk only happens one time.
        	onceBottomUp sync.Once
        
        	// onceTopDown makes sure the top-down resolve walk only happens one time.
        	onceTopDown sync.Once
        
        	// shippedNodes caches the results of a full walk of nodes identifying targets
        	// distributed either directly or as derivative works. (creation guarded by mu)
        	shippedNodes *TargetNodeSet
        
        	// mu guards against concurrent update.
        	mu sync.Mutex
        }
        
        // Edges returns the list of edges in the graph. (unordered)
        func (lg *LicenseGraph) Edges() TargetEdgeList {
        	edges := make(TargetEdgeList, 0, len(lg.edges))
        	edges = append(edges, lg.edges...)
        	return edges
        }
        
        // Targets returns the list of target nodes in the graph. (unordered)
        func (lg *LicenseGraph) Targets() TargetNodeList {
        	targets := make(TargetNodeList, 0, len(lg.targets))
        	for _, target := range lg.targets {
        		targets = append(targets, target)
        	}
        	return targets
        }
        
        // TargetNames returns the list of target node names in the graph. (unordered)
        func (lg *LicenseGraph) TargetNames() []string {
        	targets := make([]string, 0, len(lg.targets))
        	for target := range lg.targets {
        		targets = append(targets, target)
        	}
        	return targets
        }
        
        // compliance-only LicenseGraph methods
        
        // newLicenseGraph constructs a new, empty instance of LicenseGraph.
        func newLicenseGraph() *LicenseGraph {
        	return &LicenseGraph{
        		rootFiles: []string{},
        		targets:   make(map[string]*TargetNode),
        	}
        }
        
        // TargetEdge describes a directed, annotated edge from a target to a
        // dependency. (immutable)
        //
        // A LicenseGraph, above, is a set of TargetEdges.
        //
        // i.e. `Target` depends on `Dependency` in the manner described by
        // `Annotations`.
        type TargetEdge struct {
        	// target and dependency identify the nodes connected by the edge.
        	target, dependency *TargetNode
        
        	// annotations identifies the set of compliance-relevant annotations describing the edge.
        	annotations TargetEdgeAnnotations
        }
        
        // Target identifies the target that depends on the dependency.
        //
        // Target needs Dependency to build.
        func (e *TargetEdge) Target() *TargetNode {
        	return e.target
        }
        
        // Dependency identifies the target depended on by the target.
        //
        // Dependency builds without Target, but Target needs Dependency to build.
        func (e *TargetEdge) Dependency() *TargetNode {
        	return e.dependency
        }
        
        // Annotations describes the type of edge by the set of annotations attached to
        // it.
        //
        // Only annotations prescribed by policy have any meaning for licensing, and
        // the meaning for licensing is likewise prescribed by policy. Other annotations
        // are preserved and ignored by policy.
        func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
        	return e.annotations
        }
        
        // IsRuntimeDependency returns true for edges representing shared libraries
        // linked dynamically at runtime.
        func (e *TargetEdge) IsRuntimeDependency() bool {
        	return edgeIsDynamicLink(e)
        }
        
        // IsDerivation returns true for edges where the target is a derivative
        // work of dependency.
        func (e *TargetEdge) IsDerivation() bool {
        	return edgeIsDerivation(e)
        }
        
        // IsBuildTool returns true for edges where the target is built
        // by dependency.
        func (e *TargetEdge) IsBuildTool() bool {
        	return !edgeIsDerivation(e) && !edgeIsDynamicLink(e)
        }
        
        // String returns a human-readable string representation of the edge.
        func (e *TargetEdge) String() string {
        	return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
        }
        
        // TargetEdgeList orders lists of edges by target then dependency then annotations.
        type TargetEdgeList []*TargetEdge
        
        // Len returns the count of the elmements in the list.
        func (l TargetEdgeList) Len() int { return len(l) }
        
        // Swap rearranges 2 elements so that each occupies the other's former position.
        func (l TargetEdgeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographically less than the `j`th.
        func (l TargetEdgeList) Less(i, j int) bool {
        	namei := l[i].target.name
        	namej := l[j].target.name
        	if namei == namej {
        		namei = l[i].dependency.name
        		namej = l[j].dependency.name
        	}
        	if namei == namej {
        		return l[i].annotations.Compare(l[j].annotations) < 0
        	}
        	return namei < namej
        }
        
        // TargetEdgePathSegment describes a single arc in a TargetPath associating the
        // edge with a context `ctx` defined by whatever process is creating the path.
        type TargetEdgePathSegment struct {
        	edge *TargetEdge
        	ctx  interface{}
        }
        
        // Target identifies the target that depends on the dependency.
        //
        // Target needs Dependency to build.
        func (s TargetEdgePathSegment) Target() *TargetNode {
        	return s.edge.target
        }
        
        // Dependency identifies the target depended on by the target.
        //
        // Dependency builds without Target, but Target needs Dependency to build.
        func (s TargetEdgePathSegment) Dependency() *TargetNode {
        	return s.edge.dependency
        }
        
        // Edge describes the target edge.
        func (s TargetEdgePathSegment) Edge() *TargetEdge {
        	return s.edge
        }
        
        // Annotations describes the type of edge by the set of annotations attached to
        // it.
        //
        // Only annotations prescribed by policy have any meaning for licensing, and
        // the meaning for licensing is likewise prescribed by policy. Other annotations
        // are preserved and ignored by policy.
        func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
        	return s.edge.annotations
        }
        
        // Context returns the context associated with the path segment. The type and
        // value of the context defined by the process creating the path.
        func (s TargetEdgePathSegment) Context() interface{} {
        	return s.ctx
        }
        
        // String returns a human-readable string representation of the edge.
        func (s TargetEdgePathSegment) String() string {
        	return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
        }
        
        // TargetEdgePath describes a sequence of edges starting at a root and ending
        // at some final dependency.
        type TargetEdgePath []TargetEdgePathSegment
        
        // NewTargetEdgePath creates a new, empty path with capacity `cap`.
        func NewTargetEdgePath(cap int) *TargetEdgePath {
        	p := make(TargetEdgePath, 0, cap)
        	return &p
        }
        
        // Push appends a new edge to the list verifying that the target of the new
        // edge is the dependency of the prior.
        func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
        	if len(*p) == 0 {
        		*p = append(*p, TargetEdgePathSegment{edge, ctx})
        		return
        	}
        	if (*p)[len(*p)-1].edge.dependency != edge.target {
        		panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
        	}
        	*p = append(*p, TargetEdgePathSegment{edge, ctx})
        }
        
        // Pop shortens the path by 1 edge.
        func (p *TargetEdgePath) Pop() {
        	if len(*p) == 0 {
        		panic(fmt.Errorf("attempt to remove edge from empty path"))
        	}
        	*p = (*p)[:len(*p)-1]
        }
        
        // Clear makes the path length 0.
        func (p *TargetEdgePath) Clear() {
        	*p = (*p)[:0]
        }
        
        // Copy makes a new path with the same value.
        func (p *TargetEdgePath) Copy() *TargetEdgePath {
        	result := make(TargetEdgePath, 0, len(*p))
        	for _, e := range *p {
        		result = append(result, e)
        	}
        	return &result
        }
        
        // String returns a string representation of the path: [n1 -> n2 -> ... -> nn].
        func (p *TargetEdgePath) String() string {
        	if p == nil {
        		return "nil"
        	}
        	if len(*p) == 0 {
        		return "[]"
        	}
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "[")
        	for _, s := range *p {
        		fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
        	}
        	lastSegment := (*p)[len(*p)-1]
        	fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
        	return sb.String()
        }
        
        // TargetNode describes a module or target identified by the name of a specific
        // metadata file. (immutable)
        //
        // Each metadata file corresponds to a Soong module or to a Make target.
        //
        // A target node can appear as the target or as the dependency in edges.
        // Most target nodes appear as both target in one edge and as dependency in
        // other edges.
        type TargetNode targetNode
        
        // Name returns the string that identifies the target node.
        // i.e. path to license metadata file
        func (tn *TargetNode) Name() string {
        	return tn.name
        }
        
        // Dependencies returns the list of edges to dependencies of `tn`.
        func (tn *TargetNode) Dependencies() TargetEdgeList {
        	edges := make(TargetEdgeList, 0, len(tn.edges))
        	edges = append(edges, tn.edges...)
        	return edges
        }
        
        // PackageName returns the string that identifes the package for the target.
        func (tn *TargetNode) PackageName() string {
        	return tn.proto.GetPackageName()
        }
        
        // ModuleName returns the module name of the target.
        func (tn *TargetNode) ModuleName() string {
        	return tn.proto.GetModuleName()
        }
        
        // Projects returns the projects defining the target node. (unordered)
        //
        // In an ideal world, only 1 project defines a target, but the interaction
        // between Soong and Make for a variety of architectures and for host versus
        // product means a module is sometimes defined more than once.
        func (tn *TargetNode) Projects() []string {
        	return append([]string{}, tn.proto.Projects...)
        }
        
        // LicenseConditions returns a copy of the set of license conditions
        // originating at the target. The values that appear and how each is resolved
        // is a matter of policy. (unordered)
        //
        // e.g. notice or proprietary
        func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
        	return tn.licenseConditions
        }
        
        // LicenseTexts returns the paths to the files containing the license texts for
        // the target. (unordered)
        func (tn *TargetNode) LicenseTexts() []string {
        	return append([]string{}, tn.proto.LicenseTexts...)
        }
        
        // IsContainer returns true if the target represents a container that merely
        // aggregates other targets.
        func (tn *TargetNode) IsContainer() bool {
        	return tn.proto.GetIsContainer()
        }
        
        // Built returns the list of files built by the module or target. (unordered)
        func (tn *TargetNode) Built() []string {
        	return append([]string{}, tn.proto.Built...)
        }
        
        // Installed returns the list of files installed by the module or target.
        // (unordered)
        func (tn *TargetNode) Installed() []string {
        	return append([]string{}, tn.proto.Installed...)
        }
        
        // TargetFiles returns the list of files built or installed by the module or
        // target. (unordered)
        func (tn *TargetNode) TargetFiles() []string {
        	return append(tn.proto.Built, tn.proto.Installed...)
        }
        
        // InstallMap returns the list of path name transformations to make to move
        // files from their original location in the file system to their destination
        // inside a container. (unordered)
        func (tn *TargetNode) InstallMap() []InstallMap {
        	result := make([]InstallMap, 0, len(tn.proto.InstallMap))
        	for _, im := range tn.proto.InstallMap {
        		result = append(result, InstallMap{im.GetFromPath(), im.GetContainerPath()})
        	}
        	return result
        }
        
        // Sources returns the list of file names depended on by the target, which may
        // be a proper subset of those made available by dependency modules.
        // (unordered)
        func (tn *TargetNode) Sources() []string {
        	return append([]string{}, tn.proto.Sources...)
        }
        
        // InstallMap describes the mapping from an input filesystem file to file in a
        // container.
        type InstallMap struct {
        	// FromPath is the input path on the filesystem.
        	FromPath string
        
        	// ContainerPath is the path to the same file inside the container or
        	// installed location.
        	ContainerPath string
        }
        
        // TargetEdgeAnnotations describes an immutable set of annotations attached to
        // an edge from a target to a dependency.
        //
        // Annotations typically distinguish between static linkage versus dynamic
        // versus tools that are used at build time but are not linked in any way.
        type TargetEdgeAnnotations struct {
        	annotations map[string]struct{}
        }
        
        // newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
        func newEdgeAnnotations() TargetEdgeAnnotations {
        	return TargetEdgeAnnotations{make(map[string]struct{})}
        }
        
        // HasAnnotation returns true if an annotation `ann` is in the set.
        func (ea TargetEdgeAnnotations) HasAnnotation(ann string) bool {
        	_, ok := ea.annotations[ann]
        	return ok
        }
        
        // Compare orders TargetAnnotations returning:
        // -1 when ea < other,
        // +1 when ea > other, and
        // 0 when ea == other.
        func (ea TargetEdgeAnnotations) Compare(other TargetEdgeAnnotations) int {
        	a1 := ea.AsList()
        	a2 := other.AsList()
        	sort.Strings(a1)
        	sort.Strings(a2)
        	for k := 0; k < len(a1) && k < len(a2); k++ {
        		if a1[k] < a2[k] {
        			return -1
        		}
        		if a1[k] > a2[k] {
        			return 1
        		}
        	}
        	if len(a1) < len(a2) {
        		return -1
        	}
        	if len(a1) > len(a2) {
        		return 1
        	}
        	return 0
        }
        
        // AsList returns the list of annotation names attached to the edge.
        // (unordered)
        func (ea TargetEdgeAnnotations) AsList() []string {
        	l := make([]string, 0, len(ea.annotations))
        	for ann := range ea.annotations {
        		l = append(l, ann)
        	}
        	return l
        }
        
        // TargetNodeSet describes a set of distinct nodes in a license graph.
        type TargetNodeSet map[*TargetNode]struct{}
        
        // Contains returns true when `target` is an element of the set.
        func (ts TargetNodeSet) Contains(target *TargetNode) bool {
        	_, isPresent := ts[target]
        	return isPresent
        }
        
        // Names returns the array of target node namess in the set. (unordered)
        func (ts TargetNodeSet) Names() []string {
        	result := make([]string, 0, len(ts))
        	for tn := range ts {
        		result = append(result, tn.name)
        	}
        	return result
        }
        
        // String returns a human-readable string representation of the set.
        func (ts TargetNodeSet) String() string {
        	return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
        }
        
        // TargetNodeList orders a list of targets by name.
        type TargetNodeList []*TargetNode
        
        // Len returns the count of elements in the list.
        func (l TargetNodeList) Len() int { return len(l) }
        
        // Swap rearranges 2 elements so that each occupies the other's former position.
        func (l TargetNodeList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographicallt less than the `j`th.
        func (l TargetNodeList) Less(i, j int) bool {
        	return l[i].name < l[j].name
        }
        
        // String returns a string representation of the list.
        func (l TargetNodeList) String() string {
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "[")
        	sep := ""
        	for _, tn := range l {
        		fmt.Fprintf(&sb, "%s%s", sep, tn.name)
        		sep = " "
        	}
        	fmt.Fprintf(&sb, "]")
        	return sb.String()
        }
        
        // Names returns an array the names of the nodes in the same order as the nodes in the list.
        func (l TargetNodeList) Names() []string {
        	result := make([]string, 0, len(l))
        	for _, tn := range l {
        		result = append(result, tn.name)
        	}
        	return result
        }
        
        
        ================================================
        FILE: tools/compliance/noticeindex.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"crypto/md5"
        	"fmt"
        	"io"
        	"io/fs"
        	"net/url"
        	"path/filepath"
        	"regexp"
        	"sort"
        	"strings"
        
        	"android/soong/tools/compliance/projectmetadata"
        )
        
        var (
        	licensesPathRegexp = regexp.MustCompile(`licen[cs]es?/`)
        )
        
        // NoticeIndex transforms license metadata into license text hashes, library
        // names, and install paths indexing them for fast lookup/iteration.
        type NoticeIndex struct {
        	// lg identifies the license graph to which the index applies.
        	lg *LicenseGraph
        	// pmix indexes project metadata
        	pmix *projectmetadata.Index
        	// rs identifies the set of resolutions upon which the index is based.
        	rs ResolutionSet
        	// shipped identifies the set of target nodes shipped directly or as derivative works.
        	shipped TargetNodeSet
        	// rootFS locates the root of the file system from which to read the files.
        	rootFS fs.FS
        	// hash maps license text filenames to content hashes
        	hash map[string]hash
        	// text maps content hashes to content
        	text map[hash][]byte
        	// hashLibInstall maps hashes to libraries to install paths.
        	hashLibInstall map[hash]map[string]map[string]struct{}
        	// installHashLib maps install paths to libraries to hashes.
        	installHashLib map[string]map[hash]map[string]struct{}
        	// libHash maps libraries to hashes.
        	libHash map[string]map[hash]struct{}
        	// targetHash maps target nodes to hashes.
        	targetHashes map[*TargetNode]map[hash]struct{}
        	// projectName maps project directory names to project name text.
        	projectName map[string]string
        	// files lists all the files accessed during indexing
        	files []string
        }
        
        // IndexLicenseTexts creates a hashed index of license texts for `lg` and `rs`
        // using the files rooted at `rootFS`.
        func IndexLicenseTexts(rootFS fs.FS, lg *LicenseGraph, rs ResolutionSet) (*NoticeIndex, error) {
        	if rs == nil {
        		rs = ResolveNotices(lg)
        	}
        	ni := &NoticeIndex{
        		lg:             lg,
        		pmix:           projectmetadata.NewIndex(rootFS),
        		rs:             rs,
        		shipped:        ShippedNodes(lg),
        		rootFS:         rootFS,
        		hash:           make(map[string]hash),
        		text:           make(map[hash][]byte),
        		hashLibInstall: make(map[hash]map[string]map[string]struct{}),
        		installHashLib: make(map[string]map[hash]map[string]struct{}),
        		libHash:        make(map[string]map[hash]struct{}),
        		targetHashes:   make(map[*TargetNode]map[hash]struct{}),
        		projectName:    make(map[string]string),
        	}
        
        	// index adds all license texts for `tn` to the index.
        	index := func(tn *TargetNode) (map[hash]struct{}, error) {
        		if hashes, ok := ni.targetHashes[tn]; ok {
        			return hashes, nil
        		}
        		hashes := make(map[hash]struct{})
        		for _, text := range tn.LicenseTexts() {
        			fname := strings.SplitN(text, ":", 2)[0]
        			if _, ok := ni.hash[fname]; !ok {
        				err := ni.addText(fname)
        				if err != nil {
        					return nil, err
        				}
        			}
        			hash := ni.hash[fname]
        			if _, ok := hashes[hash]; !ok {
        				hashes[hash] = struct{}{}
        			}
        		}
        		ni.targetHashes[tn] = hashes
        		return hashes, nil
        	}
        
        	link := func(tn *TargetNode, hashes map[hash]struct{}, installPaths []string) error {
        		for h := range hashes {
        			libName, err := ni.getLibName(tn, h)
        			if err != nil {
        				return err
        			}
        			if _, ok := ni.libHash[libName]; !ok {
        				ni.libHash[libName] = make(map[hash]struct{})
        			}
        			if _, ok := ni.hashLibInstall[h]; !ok {
        				ni.hashLibInstall[h] = make(map[string]map[string]struct{})
        			}
        			if _, ok := ni.libHash[libName][h]; !ok {
        				ni.libHash[libName][h] = struct{}{}
        			}
        			for _, installPath := range installPaths {
        				if _, ok := ni.installHashLib[installPath]; !ok {
        					ni.installHashLib[installPath] = make(map[hash]map[string]struct{})
        					ni.installHashLib[installPath][h] = make(map[string]struct{})
        					ni.installHashLib[installPath][h][libName] = struct{}{}
        				} else if _, ok = ni.installHashLib[installPath][h]; !ok {
        					ni.installHashLib[installPath][h] = make(map[string]struct{})
        					ni.installHashLib[installPath][h][libName] = struct{}{}
        				} else if _, ok = ni.installHashLib[installPath][h][libName]; !ok {
        					ni.installHashLib[installPath][h][libName] = struct{}{}
        				}
        				if _, ok := ni.hashLibInstall[h]; !ok {
        					ni.hashLibInstall[h] = make(map[string]map[string]struct{})
        					ni.hashLibInstall[h][libName] = make(map[string]struct{})
        					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        				} else if _, ok = ni.hashLibInstall[h][libName]; !ok {
        					ni.hashLibInstall[h][libName] = make(map[string]struct{})
        					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        				} else if _, ok = ni.hashLibInstall[h][libName][installPath]; !ok {
        					ni.hashLibInstall[h][libName][installPath] = struct{}{}
        				}
        			}
        		}
        		return nil
        	}
        
        	cacheMetadata := func(tn *TargetNode) {
        		ni.pmix.MetadataForProjects(tn.Projects()...)
        	}
        
        	// returns error from walk below.
        	var err error
        
        	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        		if err != nil {
        			return false
        		}
        		if !ni.shipped.Contains(tn) {
        			return false
        		}
        		go cacheMetadata(tn)
        		installPaths := getInstallPaths(tn, path)
        		var hashes map[hash]struct{}
        		hashes, err = index(tn)
        		if err != nil {
        			return false
        		}
        		err = link(tn, hashes, installPaths)
        		if err != nil {
        			return false
        		}
        		if tn.IsContainer() {
        			return true
        		}
        
        		for _, r := range rs.Resolutions(tn) {
        			hashes, err = index(r.actsOn)
        			if err != nil {
        				return false
        			}
        			err = link(r.actsOn, hashes, installPaths)
        			if err != nil {
        				return false
        			}
        		}
        		return false
        	})
        
        	if err != nil {
        		return nil, err
        	}
        
        	return ni, nil
        }
        
        // Hashes returns an ordered channel of the hashed license texts.
        func (ni *NoticeIndex) Hashes() chan hash {
        	c := make(chan hash)
        	go func() {
        		libs := make([]string, 0, len(ni.libHash))
        		for libName := range ni.libHash {
        			libs = append(libs, libName)
        		}
        		sort.Strings(libs)
        		hashes := make(map[hash]struct{})
        		for _, libName := range libs {
        			hl := make([]hash, 0, len(ni.libHash[libName]))
        			for h := range ni.libHash[libName] {
        				if _, ok := hashes[h]; ok {
        					continue
        				}
        				hashes[h] = struct{}{}
        				hl = append(hl, h)
        			}
        			if len(hl) > 0 {
        				sort.Sort(hashList{ni, libName, "", &hl})
        				for _, h := range hl {
        					c <- h
        				}
        			}
        		}
        		close(c)
        	}()
        	return c
        
        }
        
        // InputFiles returns the complete list of files read during indexing.
        func (ni *NoticeIndex) InputFiles() []string {
        	projectMeta := ni.pmix.AllMetadataFiles()
        	files := make([]string, 0, len(ni.files) + len(ni.lg.targets) + len(projectMeta))
        	files = append(files, ni.files...)
        	for f := range ni.lg.targets {
        		files = append(files, f)
        	}
        	files = append(files, projectMeta...)
        	return files
        }
        
        // HashLibs returns the ordered array of library names using the license text
        // hashed as `h`.
        func (ni *NoticeIndex) HashLibs(h hash) []string {
        	libs := make([]string, 0, len(ni.hashLibInstall[h]))
        	for libName := range ni.hashLibInstall[h] {
        		libs = append(libs, libName)
        	}
        	sort.Strings(libs)
        	return libs
        }
        
        // HashLibInstalls returns the ordered array of install paths referencing
        // library `libName` using the license text hashed as `h`.
        func (ni *NoticeIndex) HashLibInstalls(h hash, libName string) []string {
        	installs := make([]string, 0, len(ni.hashLibInstall[h][libName]))
        	for installPath := range ni.hashLibInstall[h][libName] {
        		installs = append(installs, installPath)
        	}
        	sort.Strings(installs)
        	return installs
        }
        
        // InstallPaths returns the ordered channel of indexed install paths.
        func (ni *NoticeIndex) InstallPaths() chan string {
        	c := make(chan string)
        	go func() {
        		paths := make([]string, 0, len(ni.installHashLib))
        		for path := range ni.installHashLib {
        			paths = append(paths, path)
        		}
        		sort.Strings(paths)
        		for _, installPath := range paths {
        			c <- installPath
        		}
        		close(c)
        	}()
        	return c
        }
        
        // InstallHashes returns the ordered array of hashes attached to `installPath`.
        func (ni *NoticeIndex) InstallHashes(installPath string) []hash {
        	result := make([]hash, 0, len(ni.installHashLib[installPath]))
        	for h := range ni.installHashLib[installPath] {
        		result = append(result, h)
        	}
        	if len(result) > 0 {
        		sort.Sort(hashList{ni, "", installPath, &result})
        	}
        	return result
        }
        
        // InstallHashLibs returns the ordered array of library names attached to
        // `installPath` as hash `h`.
        func (ni *NoticeIndex) InstallHashLibs(installPath string, h hash) []string {
        	result := make([]string, 0, len(ni.installHashLib[installPath][h]))
        	for libName := range ni.installHashLib[installPath][h] {
        		result = append(result, libName)
        	}
        	sort.Strings(result)
        	return result
        }
        
        // Libraries returns the ordered channel of indexed library names.
        func (ni *NoticeIndex) Libraries() chan string {
        	c := make(chan string)
        	go func() {
        		libs := make([]string, 0, len(ni.libHash))
        		for lib := range ni.libHash {
        			libs = append(libs, lib)
        		}
        		sort.Strings(libs)
        		for _, lib := range libs {
        			c <- lib
        		}
        		close(c)
        	}()
        	return c
        }
        
        // HashText returns the file content of the license text hashed as `h`.
        func (ni *NoticeIndex) HashText(h hash) []byte {
        	return ni.text[h]
        }
        
        // getLibName returns the name of the library associated with `noticeFor`.
        func (ni *NoticeIndex) getLibName(noticeFor *TargetNode, h hash) (string, error) {
        	for _, text := range noticeFor.LicenseTexts() {
        		if !strings.Contains(text, ":") {
        			if ni.hash[text].key != h.key {
        				continue
        			}
        			ln, err := ni.checkMetadataForLicenseText(noticeFor, text)
        			if err != nil {
        				return "", err
        			}
        			if len(ln) > 0 {
        				return ln, nil
        			}
        			continue
        		}
        
        		fields := strings.SplitN(text, ":", 2)
        		fname, pname := fields[0], fields[1]
        		if ni.hash[fname].key != h.key {
        			continue
        		}
        
        		ln, err := url.QueryUnescape(pname)
        		if err != nil {
        			continue
        		}
        		return ln, nil
        	}
        	// use name from METADATA if available
        	ln, err := ni.checkMetadata(noticeFor)
        	if err != nil {
        		return "", err
        	}
        	if len(ln) > 0 {
        		return ln, nil
        	}
        	// use package_name: from license{} module if available
        	pn := noticeFor.PackageName()
        	if len(pn) > 0 {
        		return pn, nil
        	}
        	for _, p := range noticeFor.Projects() {
        		if strings.HasPrefix(p, "prebuilts/") {
        			for _, licenseText := range noticeFor.LicenseTexts() {
        				if !strings.HasPrefix(licenseText, "prebuilts/") {
        					continue
        				}
        				if !strings.Contains(licenseText, ":") {
        					if ni.hash[licenseText].key != h.key {
        						continue
        					}
        				} else {
        					fields := strings.SplitN(licenseText, ":", 2)
        					fname := fields[0]
        					if ni.hash[fname].key != h.key {
        						continue
        					}
        				}
        				for _, safePrebuiltPrefix := range safePrebuiltPrefixes {
        					match := safePrebuiltPrefix.re.FindString(licenseText)
        					if len(match) == 0 {
        						continue
        					}
        					if safePrebuiltPrefix.strip {
        						// strip entire prefix
        						match = licenseText[len(match):]
        					} else {
        						// strip from prebuilts/ until safe prefix
        						match = licenseText[len(match)-len(safePrebuiltPrefix.prefix):]
        					}
        					// remove LICENSE or NOTICE or other filename
        					li := strings.LastIndex(match, "/")
        					if li > 0 {
        						match = match[:li]
        					}
        					// remove *licenses/ path segment and subdirectory if in path
        					if offsets := licensesPathRegexp.FindAllStringIndex(match, -1); offsets != nil && offsets[len(offsets)-1][0] > 0 {
        						match = match[:offsets[len(offsets)-1][0]]
        						li = strings.LastIndex(match, "/")
        						if li > 0 {
        							match = match[:li]
        						}
        					}
        					return match, nil
        				}
        				break
        			}
        		}
        		for _, safePathPrefix := range safePathPrefixes {
        			if strings.HasPrefix(p, safePathPrefix.prefix) {
        				if safePathPrefix.strip {
        					return p[len(safePathPrefix.prefix):], nil
        				} else {
        					return p, nil
        				}
        			}
        		}
        	}
        	// strip off [./]meta_lic from license metadata path and extract base name
        	n := noticeFor.name[:len(noticeFor.name)-9]
        	li := strings.LastIndex(n, "/")
        	if li > 0 {
        		n = n[li+1:]
        	}
        	fi := strings.Index(n, "@")
        	if fi > 0 {
        		n = n[:fi]
        	}
        	return n, nil
        }
        
        // checkMetadata tries to look up a library name from a METADATA file associated with `noticeFor`.
        func (ni *NoticeIndex) checkMetadata(noticeFor *TargetNode) (string, error) {
        	pms, err := ni.pmix.MetadataForProjects(noticeFor.Projects()...)
        	if err != nil {
        		return "", err
        	}
        	for _, pm := range pms {
        		name := pm.VersionedName()
        		if name != "" {
        			return name, nil
        		}
        	}
        	return "", nil
        }
        
        // checkMetadataForLicenseText
        func (ni *NoticeIndex) checkMetadataForLicenseText(noticeFor *TargetNode, licenseText string) (string, error) {
        	p := ""
        	for _, proj := range noticeFor.Projects() {
        		if strings.HasPrefix(licenseText, proj) {
        			p = proj
        		}
        	}
        	if len(p) == 0 {
        		p = filepath.Dir(licenseText)
        		for {
        			fi, err := fs.Stat(ni.rootFS, filepath.Join(p, ".git"))
        			if err == nil && fi.IsDir() {
        				break
        			}
        			if strings.Contains(p, "/") && p != "/" {
        				p = filepath.Dir(p)
        				continue
        			}
        			return "", nil
        		}
        	}
        	pms, err := ni.pmix.MetadataForProjects(p)
        	if err != nil {
        		return "", err
        	}
        	if pms == nil {
        		return "", nil
        	}
        	return pms[0].VersionedName(), nil
        }
        
        // addText reads and indexes the content of a license text file.
        func (ni *NoticeIndex) addText(file string) error {
        	f, err := ni.rootFS.Open(filepath.Clean(file))
        	if err != nil {
        		return fmt.Errorf("error opening license text file %q: %w", file, err)
        	}
        
        	// read the file
        	text, err := io.ReadAll(f)
        	if err != nil {
        		return fmt.Errorf("error reading license text file %q: %w", file, err)
        	}
        
        	hash := hash{fmt.Sprintf("%x", md5.Sum(text))}
        	ni.hash[file] = hash
        	if _, alreadyPresent := ni.text[hash]; !alreadyPresent {
        		ni.text[hash] = text
        	}
        
        	ni.files = append(ni.files, file)
        
        	return nil
        }
        
        // getInstallPaths returns the names of the used dependencies mapped to their
        // installed locations.
        func getInstallPaths(attachesTo *TargetNode, path TargetEdgePath) []string {
        	if len(path) == 0 {
        		installs := attachesTo.Installed()
        		if 0 == len(installs) {
        			installs = attachesTo.Built()
        		}
        		return installs
        	}
        
        	var getInstalls func(path TargetEdgePath) []string
        
        	getInstalls = func(path TargetEdgePath) []string {
        		// deps contains the output targets from the dependencies in the path
        		var deps []string
        		if len(path) > 1 {
        			// recursively get the targets from the sub-path skipping 1 path segment
        			deps = getInstalls(path[1:])
        		} else {
        			// stop recursion at 1 path segment
        			deps = path[0].Dependency().TargetFiles()
        		}
        		size := 0
        		prefixes := path[0].Target().TargetFiles()
        		installMap := path[0].Target().InstallMap()
        		sources := path[0].Target().Sources()
        		for _, dep := range deps {
        			found := false
        			for _, source := range sources {
        				if strings.HasPrefix(dep, source) {
        					found = true
        					break
        				}
        			}
        			if !found {
        				continue
        			}
        			for _, im := range installMap {
        				if strings.HasPrefix(dep, im.FromPath) {
        					size += len(prefixes)
        					break
        				}
        			}
        		}
        
        		installs := make([]string, 0, size)
        		for _, dep := range deps {
        			found := false
        			for _, source := range sources {
        				if strings.HasPrefix(dep, source) {
        					found = true
        					break
        				}
        			}
        			if !found {
        				continue
        			}
        			for _, im := range installMap {
        				if strings.HasPrefix(dep, im.FromPath) {
        					for _, prefix := range prefixes {
        						installs = append(installs, prefix+im.ContainerPath+dep[len(im.FromPath):])
        					}
        					break
        				}
        			}
        		}
        		return installs
        	}
        	allInstalls := getInstalls(path)
        	installs := path[0].Target().Installed()
        	if len(installs) == 0 {
        		return allInstalls
        	}
        	result := make([]string, 0, len(allInstalls))
        	for _, install := range allInstalls {
        		for _, prefix := range installs {
        			if strings.HasPrefix(install, prefix) {
        				result = append(result, install)
        			}
        		}
        	}
        	return result
        }
        
        // hash is an opaque string derived from md5sum.
        type hash struct {
        	key string
        }
        
        // String returns the hexadecimal representation of the hash.
        func (h hash) String() string {
        	return h.key
        }
        
        // hashList orders an array of hashes
        type hashList struct {
        	ni          *NoticeIndex
        	libName     string
        	installPath string
        	hashes      *[]hash
        }
        
        // Len returns the count of elements in the slice.
        func (l hashList) Len() int { return len(*l.hashes) }
        
        // Swap rearranges 2 elements of the slice so that each occupies the other's
        // former position.
        func (l hashList) Swap(i, j int) { (*l.hashes)[i], (*l.hashes)[j] = (*l.hashes)[j], (*l.hashes)[i] }
        
        // Less returns true when the `i`th element is lexicographically less than
        // the `j`th element.
        func (l hashList) Less(i, j int) bool {
        	var insti, instj int
        	if len(l.libName) > 0 {
        		insti = len(l.ni.hashLibInstall[(*l.hashes)[i]][l.libName])
        		instj = len(l.ni.hashLibInstall[(*l.hashes)[j]][l.libName])
        	} else {
        		libsi := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[i])
        		libsj := l.ni.InstallHashLibs(l.installPath, (*l.hashes)[j])
        		libsis := strings.Join(libsi, " ")
        		libsjs := strings.Join(libsj, " ")
        		if libsis != libsjs {
        			return libsis < libsjs
        		}
        	}
        	if insti == instj {
        		leni := len(l.ni.text[(*l.hashes)[i]])
        		lenj := len(l.ni.text[(*l.hashes)[j]])
        		if leni == lenj {
        			// all else equal, just order by hash value
        			return (*l.hashes)[i].key < (*l.hashes)[j].key
        		}
        		// put shortest texts first within same # of installs
        		return leni < lenj
        	}
        	// reverse order of # installs so that most popular appears first
        	return instj < insti
        }
        
        
        ================================================
        FILE: tools/compliance/policy_policy.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"regexp"
        	"strings"
        )
        
        var (
        	// RecognizedAnnotations identifies the set of annotations that have
        	// meaning for compliance policy.
        	RecognizedAnnotations = map[string]string{
        		// used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
        		"static":    "static",
        		"dynamic":   "dynamic",
        		"toolchain": "toolchain",
        	}
        
        	// safePathPrefixes maps the path prefixes presumed not to contain any
        	// proprietary or confidential pathnames to whether to strip the prefix
        	// from the path when used as the library name for notices.
        	safePathPrefixes = []safePathPrefixesType{
        		{"external/", true},
        		{"art/", false},
        		{"build/", false},
        		{"cts/", false},
        		{"dalvik/", false},
        		{"developers/", false},
        		{"development/", false},
        		{"frameworks/", false},
        		{"packages/", true},
        		{"prebuilts/module_sdk/", true},
        		{"prebuilts/", false},
        		{"sdk/", false},
        		{"system/", false},
        		{"test/", false},
        		{"toolchain/", false},
        		{"tools/", false},
        	}
        
        	// safePrebuiltPrefixes maps the regular expression to match a prebuilt
        	// containing the path of a safe prefix to the safe prefix.
        	safePrebuiltPrefixes []safePrebuiltPrefixesType
        
        	// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
        	ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
        
        	// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
        	ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
        
        	// ImpliesNotice lists the condition names implying a notice or attribution policy.
        	ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
        		RestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition)
        
        	// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
        	ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
        
        	// Restricted lists the condition names implying an infectious source-sharing policy.
        	ImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition)
        
        	// ImpliesProprietary lists the condition names implying a confidentiality policy.
        	ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
        
        	// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
        	ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
        
        	// ImpliesPrivate lists the condition names implying a source-code privacy policy.
        	ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
        
        	// ImpliesShared lists the condition names implying a source-code sharing policy.
        	ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition)
        )
        
        type safePathPrefixesType struct {
        	prefix string
        	strip  bool
        }
        
        type safePrebuiltPrefixesType struct {
        	safePathPrefixesType
        	re *regexp.Regexp
        }
        
        var (
        	anyLgpl      = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
        	versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
        	genericGpl   = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
        	ccBySa       = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
        )
        
        func init() {
        	for _, safePathPrefix := range safePathPrefixes {
        		if strings.HasPrefix(safePathPrefix.prefix, "prebuilts/") {
        			continue
        		}
        		r := regexp.MustCompile("^prebuilts/(?:runtime/mainline/)?" + safePathPrefix.prefix)
        		safePrebuiltPrefixes = append(safePrebuiltPrefixes,
        			safePrebuiltPrefixesType{safePathPrefix, r})
        	}
        }
        
        // LicenseConditionSetFromNames returns a set containing the recognized `names` and
        // silently ignoring or discarding the unrecognized `names`.
        func LicenseConditionSetFromNames(names ...string) LicenseConditionSet {
        	cs := NewLicenseConditionSet()
        	for _, name := range names {
        		if lc, ok := RecognizedConditionNames[name]; ok {
        			cs |= LicenseConditionSet(lc)
        		}
        	}
        	return cs
        }
        
        // Resolution happens in three phases:
        //
        // 1. A bottom-up traversal propagates (restricted) license conditions up to
        // targets from dendencies as needed.
        //
        // 2. For each condition of interest, a top-down traversal propagates
        // (restricted) conditions down from targets into linked dependencies.
        //
        // 3. Finally, a walk of the shipped target nodes attaches resolutions to the
        // ancestor nodes from the root down to and including the first non-container.
        //
        // e.g. If a disk image contains a binary bin1 that links a library liba, the
        // notice requirement for liba gets attached to the disk image and to bin1.
        // Because liba doesn't actually get shipped as a separate artifact, but only
        // as bits in bin1, it has no actions 'attached' to it. The actions attached
        // to the image and to bin1 'act on' liba by providing notice.
        //
        // The behavior of the 3 phases gets controlled by the 3 functions below.
        //
        // The first function controls what happens during the bottom-up propagation.
        // Restricted conditions propagate up all non-toolchain dependencies; except,
        // some do not propagate up dynamic links, which may depend on whether the
        // modules are independent.
        //
        // The second function controls what happens during the top-down propagation.
        // Restricted conditions propagate down as above with the added caveat that
        // inherited restricted conditions do not propagate from pure aggregates to
        // their dependencies.
        //
        // The final function controls which conditions apply/get attached to ancestors
        // depending on the types of dependencies involved. All conditions apply across
        // normal derivation dependencies. No conditions apply across toolchain
        // dependencies. Some restricted conditions apply across dynamic link
        // dependencies.
        //
        // Not all restricted licenses are create equal. Some have special rules or
        // exceptions. e.g. LGPL or "with classpath excption".
        
        // depConditionsPropagatingToTarget returns the conditions which propagate up an
        // edge from dependency to target.
        //
        // This function sets the policy for the bottom-up propagation and how conditions
        // flow up the graph from dependencies to targets.
        //
        // If a pure aggregation is built into a derivative work that is not a pure
        // aggregation, per policy it ceases to be a pure aggregation in the context of
        // that derivative work. The `treatAsAggregate` parameter will be false for
        // non-aggregates and for aggregates in non-aggregate contexts.
        func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
        	result := LicenseConditionSet(0x0000)
        	if edgeIsDerivation(e) {
        		result |= depConditions & ImpliesRestricted
        		return result
        	}
        	if !edgeIsDynamicLink(e) {
        		return result
        	}
        
        	result |= depConditions & LicenseConditionSet(RestrictedCondition)
        	return result
        }
        
        // targetConditionsPropagatingToDep returns the conditions which propagate down
        // an edge from target to dependency.
        //
        // This function sets the policy for the top-down traversal and how conditions
        // flow down the graph from targets to dependencies.
        //
        // If a pure aggregation is built into a derivative work that is not a pure
        // aggregation, per policy it ceases to be a pure aggregation in the context of
        // that derivative work. The `treatAsAggregate` parameter will be false for
        // non-aggregates and for aggregates in non-aggregate contexts.
        func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
        	result := targetConditions
        
        	// reverse direction -- none of these apply to things depended-on, only to targets depending-on.
        	result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
        
        	if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
        		// target is not a derivative work of dependency and is not linked to dependency
        		result = result.Difference(ImpliesRestricted)
        		return result
        	}
        	if treatAsAggregate {
        		// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
        		// Otherwise, restricted does not propagate back down to dependencies.
        		if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
        			result = result.Difference(ImpliesRestricted)
        		}
        		return result
        	}
        	if edgeIsDerivation(e) {
        		return result
        	}
        	result = result.Minus(WeaklyRestrictedCondition)
        	return result
        }
        
        // conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
        // that apply across edge `e`.
        //
        // This function sets the policy for attaching actions to ancestor nodes in the
        // final resolution walk.
        func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
        	result := universe
        	if edgeIsDerivation(e) {
        		return result
        	}
        	if !edgeIsDynamicLink(e) {
        		return NewLicenseConditionSet()
        	}
        
        	result &= LicenseConditionSet(RestrictedCondition)
        	return result
        }
        
        // edgeIsDynamicLink returns true for edges representing shared libraries
        // linked dynamically at runtime.
        func edgeIsDynamicLink(e *TargetEdge) bool {
        	return e.annotations.HasAnnotation("dynamic")
        }
        
        // edgeIsDerivation returns true for edges where the target is a derivative
        // work of dependency.
        func edgeIsDerivation(e *TargetEdge) bool {
        	isDynamic := e.annotations.HasAnnotation("dynamic")
        	isToolchain := e.annotations.HasAnnotation("toolchain")
        	return !isDynamic && !isToolchain
        }
        
        
        ================================================
        FILE: tools/compliance/policy_policy_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"fmt"
        	"sort"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance/testfs"
        )
        
        func TestPolicy_edgeConditions(t *testing.T) {
        	tests := []struct {
        		name                     string
        		edge                     annotated
        		treatAsAggregate         bool
        		otherCondition           string
        		expectedDepActions       []string
        		expectedTargetConditions []string
        	}{
        		{
        			name:                     "firstparty",
        			edge:                     annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "notice",
        			edge:                     annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name: "fponlgpl",
        			edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			expectedDepActions: []string{
        				"apacheBin.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked",
        				"lgplLib.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked",
        			},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "fponlgpldynamic",
        			edge:                     annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name: "fpongpl",
        			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			expectedDepActions: []string{
        				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        			},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name: "fpongpldynamic",
        			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			expectedDepActions: []string{
        				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        			},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "independentmodule",
        			edge:                     annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "independentmodulestatic",
        			edge:                     annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "dependentmodule",
        			edge:                     annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        
        		{
        			name:                     "lgplonfp",
        			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_if_statically_linked"},
        		},
        		{
        			name:                     "lgplonfpdynamic",
        			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "gplonfp",
        			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        		},
        		{
        			name:                     "gplcontainer",
        			edge:                     annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			treatAsAggregate:         true,
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
        		},
        		{
        			name:             "gploncontainer",
        			edge:             annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			treatAsAggregate: true,
        			otherCondition:   "gplLib.meta_lic:restricted",
        			expectedDepActions: []string{
        				"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
        				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
        				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        			},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:             "gplonbin",
        			edge:             annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			treatAsAggregate: false,
        			otherCondition:   "gplLib.meta_lic:restricted",
        			expectedDepActions: []string{
        				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
        				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
        				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        			},
        			expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
        		},
        		{
        			name:                     "gplonfpdynamic",
        			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        		},
        		{
        			name:                     "independentmodulereverse",
        			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "independentmodulereversestatic",
        			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "dependentmodulereverse",
        			edge:                     annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name: "ponr",
        			edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			expectedDepActions: []string{
        				"proprietary.meta_lic:gplLib.meta_lic:restricted",
        				"gplLib.meta_lic:gplLib.meta_lic:restricted",
        			},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "ronp",
        			edge:                     annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
        		},
        		{
        			name:                     "noticeonb_e_o",
        			edge:                     annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "b_e_oonnotice",
        			edge:                     annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "noticeonrecip",
        			edge:                     annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        		{
        			name:                     "reciponnotice",
        			edge:                     annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			expectedDepActions:       []string{},
        			expectedTargetConditions: []string{},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			fs := make(testfs.TestFS)
        			stderr := &bytes.Buffer{}
        			target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n  file: \"%s\"\n", tt.edge.dep)
        			for _, ann := range tt.edge.annotations {
        				target += fmt.Sprintf("  annotations: \"%s\"\n", ann)
        			}
        			fs[tt.edge.target] = []byte(target + "}\n")
        			fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
        			lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
        			if err != nil {
        				t.Errorf("unexpected error reading graph: %s", err)
        				return
        			}
        			edge := lg.Edges()[0]
        			// simulate a condition inherited from another edge/dependency.
        			otherTarget := ""
        			otherCondition := ""
        			var otn *TargetNode
        			if len(tt.otherCondition) > 0 {
        				fields := strings.Split(tt.otherCondition, ":")
        				otherTarget = fields[0]
        				otherCondition = fields[1]
        				otn = &TargetNode{name: otherTarget}
        				// other target must exist in graph
        				lg.targets[otherTarget] = otn
        				otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])
        			}
        			targets := make(map[string]*TargetNode)
        			targets[edge.target.name] = edge.target
        			targets[edge.dependency.name] = edge.dependency
        			if otn != nil {
        				targets[otn.name] = otn
        			}
        			if tt.expectedDepActions != nil {
        				t.Run("depConditionsPropagatingToTarget", func(t *testing.T) {
        					depConditions := edge.dependency.LicenseConditions()
        					if otherTarget != "" {
        						// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
        						otherCs := otn.LicenseConditions()
        						depConditions |= otherCs
        					}
        					t.Logf("calculate target actions for edge=%s, dep conditions=%#v %s, treatAsAggregate=%v", edge.String(), depConditions, depConditions, tt.treatAsAggregate)
        					csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)
        					t.Logf("calculated target conditions as %#v %s", csActual, csActual)
        					csExpected := NewLicenseConditionSet()
        					for _, triple := range tt.expectedDepActions {
        						fields := strings.Split(triple, ":")
        						expectedConditions := NewLicenseConditionSet()
        						for _, cname := range fields[2:] {
        							expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])
        						}
        						csExpected |= expectedConditions
        					}
        					t.Logf("expected target conditions as %#v %s", csExpected, csExpected)
        					if csActual != csExpected {
        						t.Errorf("unexpected license conditions: got %#v, want %#v", csActual, csExpected)
        					}
        				})
        			}
        			if tt.expectedTargetConditions != nil {
        				t.Run("targetConditionsPropagatingToDep", func(t *testing.T) {
        					targetConditions := edge.target.LicenseConditions()
        					if otherTarget != "" {
        						targetConditions = targetConditions.Union(otn.licenseConditions)
        					}
        					t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate)
        					cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate, AllResolutions)
        					t.Logf("calculated dep conditions as %v", cs.Names())
        					actual := cs.Names()
        					sort.Strings(actual)
        					expected := make([]string, 0)
        					for _, expectedDepCondition := range tt.expectedTargetConditions {
        						expected = append(expected, strings.Split(expectedDepCondition, ":")[1])
        					}
        					sort.Strings(expected)
        					if len(actual) != len(expected) {
        						t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
        							actual, len(actual), expected, len(expected))
        					} else {
        						for i := 0; i < len(actual); i++ {
        							if actual[i] != expected[i] {
        								t.Errorf("unexpected target condition at element %d: got %q, want %q",
        									i, actual[i], expected[i])
        							}
        						}
        					}
        				})
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolve.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        var (
        	// AllResolutions is a TraceConditions function that resolves all
        	// unfiltered license conditions.
        	AllResolutions = TraceConditions(func(tn *TargetNode) LicenseConditionSet { return tn.licenseConditions })
        )
        
        // TraceConditions is a function that returns the conditions to trace for each
        // target node `tn`.
        type TraceConditions func(tn *TargetNode) LicenseConditionSet
        
        // ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
        // propagating conditions up the graph as necessary according to the properties
        // of each edge and according to each license condition in question.
        //
        // e.g. if a "restricted" condition applies to a binary, it also applies to all
        // of the statically-linked libraries and the transitive closure of their static
        // dependencies; even if neither they nor the transitive closure of their
        // dependencies originate any "restricted" conditions. The bottom-up walk will
        // not resolve the library and its transitive closure, but the later top-down
        // walk will.
        func ResolveBottomUpConditions(lg *LicenseGraph) {
        	TraceBottomUpConditions(lg, AllResolutions)
        }
        
        // TraceBottomUpConditions performs a bottom-up walk of the LicenseGraph
        // propagating trace conditions from `conditionsFn` up the graph as necessary
        // according to the properties of each edge and according to each license
        // condition in question.
        func TraceBottomUpConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
        
        	// short-cut if already walked and cached
        	lg.onceBottomUp.Do(func() {
        		// amap identifes targets previously walked. (guarded by mu)
        		amap := make(map[*TargetNode]struct{})
        
        		var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
        
        		walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
        			priorWalkResults := func() (LicenseConditionSet, bool) {
        				if _, alreadyWalked := amap[target]; alreadyWalked {
        					if treatAsAggregate {
        						return target.resolution, true
        					}
        					if !target.pure {
        						return target.resolution, true
        					}
        					// previously walked in a pure aggregate context,
        					// needs to walk again in non-aggregate context
        				} else {
        					target.resolution |= conditionsFn(target)
        					amap[target] = struct{}{}
        				}
        				target.pure = treatAsAggregate
        				return target.resolution, false
        			}
        			cs, alreadyWalked := priorWalkResults()
        			if alreadyWalked {
        				return cs
        			}
        
        			// add all the conditions from all the dependencies
        			for _, edge := range target.edges {
        				// walk dependency to get its conditions
        				dcs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
        
        				// turn those into the conditions that apply to the target
        				dcs = depConditionsPropagatingToTarget(lg, edge, dcs, treatAsAggregate)
        				cs |= dcs
        			}
        			target.resolution |= cs
        			cs = target.resolution
        
        			// return conditions up the tree
        			return cs
        		}
        
        		// walk each of the roots
        		for _, rname := range lg.rootFiles {
        			rnode := lg.targets[rname]
        			_ = walk(rnode, rnode.IsContainer())
        		}
        	})
        }
        
        // ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
        // propagating conditions from target to dependency.
        //
        // e.g. For current policy, none of the conditions propagate from target to
        // dependency except restricted. For restricted, the policy is to share the
        // source of any libraries linked to restricted code and to provide notice.
        func ResolveTopDownConditions(lg *LicenseGraph) {
        	TraceTopDownConditions(lg, AllResolutions)
        }
        
        // TraceTopDownCondtions performs a top-down walk of the LicenseGraph
        // propagating trace conditions returned by `conditionsFn` from target to
        // dependency.
        func TraceTopDownConditions(lg *LicenseGraph, conditionsFn TraceConditions) {
        
        	// short-cut if already walked and cached
        	lg.onceTopDown.Do(func() {
        		// start with the conditions propagated up the graph
        		TraceBottomUpConditions(lg, conditionsFn)
        
        		// amap contains the set of targets already walked. (guarded by mu)
        		amap := make(map[*TargetNode]struct{})
        
        		var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
        
        		walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
        			continueWalk := func() bool {
        				if _, alreadyWalked := amap[fnode]; alreadyWalked {
        					if cs.IsEmpty() {
        						return false
        					}
        					if cs.Difference(fnode.resolution).IsEmpty() {
        						// no new conditions
        
        						// pure aggregates never need walking a 2nd time with same conditions
        						if treatAsAggregate {
        							return false
        						}
        						// non-aggregates don't need walking as non-aggregate a 2nd time
        						if !fnode.pure {
        							return false
        						}
        						// previously walked as pure aggregate; need to re-walk as non-aggregate
        					}
        				} else {
        					fnode.resolution |= conditionsFn(fnode)
        				}
        				fnode.resolution |= cs
        				fnode.pure = treatAsAggregate
        				amap[fnode] = struct{}{}
        				cs = fnode.resolution
        				return true
        			}()
        			if !continueWalk {
        				return
        			}
        			// for each dependency
        			for _, edge := range fnode.edges {
        				// dcs holds the dpendency conditions inherited from the target
        				dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate, conditionsFn)
        				dnode := edge.dependency
        				// add the conditions to the dependency
        				walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
        			}
        		}
        
        		// walk each of the roots
        		for _, rname := range lg.rootFiles {
        			rnode := lg.targets[rname]
        			// add the conditions to the root and its transitive closure
        			walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
        		}
        	})
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolve_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"sort"
        	"testing"
        )
        
        func TestResolveBottomUpConditions(t *testing.T) {
        	tests := []struct {
        		name            string
        		roots           []string
        		edges           []annotated
        		expectedActions []tcond
        	}{
        		{
        			name:  "firstparty",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartytool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartywide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "weakrestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "classpath",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:  "classpathdependent",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"dependentModule.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:  "classpathdynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:  "classpathdependentdynamic",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"dependentModule.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        
        			logGraph(lg, t)
        
        			ResolveBottomUpConditions(lg)
        			actual := asActionList(lg)
        			sort.Sort(actual)
        			t.Logf("actual: %s", actual.String())
        
        			expected := toActionList(lg, tt.expectedActions)
        			sort.Sort(expected)
        			t.Logf("expected: %s", expected.String())
        
        			if len(actual) != len(expected) {
        				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
        				return
        			}
        			for i := 0; i < len(actual); i++ {
        				if actual[i] != expected[i] {
        					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
        				}
        			}
        		})
        	}
        }
        
        func TestResolveTopDownConditions(t *testing.T) {
        	tests := []struct {
        		name            string
        		roots           []string
        		edges           []annotated
        		expectedActions []tcond
        	}{
        		{
        			name:  "firstparty",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"mitLib.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"mitLib.meta_lic", "notice"},
        				{"gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"mitBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        				{"mplLib.meta_lic", "reciprocal|restricted"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        				{"mitLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"mitBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        				{"mplLib.meta_lic", "reciprocal|restricted"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "weakrestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"mitLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"mitLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []tcond{
        				{"apacheContainer.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "classpath",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdependent",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"dependentModule.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdependentdynamic",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []tcond{
        				{"dependentModule.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        
        			logGraph(lg, t)
        
        			ResolveTopDownConditions(lg)
        			actual := asActionList(lg)
        			sort.Sort(actual)
        			t.Logf("actual: %s", actual.String())
        
        			expected := toActionList(lg, tt.expectedActions)
        			sort.Sort(expected)
        			t.Logf("expected: %s", expected.String())
        
        			if len(actual) != len(expected) {
        				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
        				return
        			}
        			for i := 0; i < len(actual); i++ {
        				if actual[i] != expected[i] {
        					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
        				}
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolvenotices.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        // ResolveNotices implements the policy for notices.
        func ResolveNotices(lg *LicenseGraph) ResolutionSet {
        	ResolveTopDownConditions(lg)
        	return WalkResolutionsForCondition(lg, ImpliesNotice)
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolvenotices_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"testing"
        )
        
        func TestResolveNotices(t *testing.T) {
        	tests := []struct {
        		name                string
        		roots               []string
        		edges               []annotated
        		expectedResolutions []res
        	}{
        		{
        			name:  "firstparty",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "firstpartydynamicshipped",
        			roots: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:  "restrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "mitLib.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "mplLib.meta_lic", "reciprocal|restricted"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "mplLib.meta_lic", "reciprocal|restricted"},
        				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        				{"mitBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamicshipped",
        			roots: []string{"apacheBin.meta_lic", "mitLib.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "restricted"},
        				{"mitLib.meta_lic", "mitLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:  "restricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "mitBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "restricteddynamicwideshipped",
        			roots: []string{"apacheContainer.meta_lic", "gplLib.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "weakrestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedtool",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestrictedtoolshipped",
        			roots: []string{"apacheBin.meta_lic", "lgplBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"apacheContainer.meta_lic", "mitLib.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestrictedwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicshipped",
        			roots: []string{"apacheBin.meta_lic", "lgplLib.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicdeepshipped",
        			roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicwide",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "weakrestricteddynamicwideshipped",
        			roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "classpath",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdependent",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdynamicshipped",
        			roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "mitLib.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:  "classpathdependentdynamic",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:  "classpathdependentdynamicshipped",
        			roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        				{"dependentModule.meta_lic", "mitLib.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        			actualRs := ResolveNotices(lg)
        			checkSame(actualRs, expectedRs, t)
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolveprivacy.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        // ResolveSourcePrivacy implements the policy for source privacy.
        func ResolveSourcePrivacy(lg *LicenseGraph) ResolutionSet {
        	ResolveTopDownConditions(lg)
        	return WalkResolutionsForCondition(lg, ImpliesPrivate)
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolveprivacy_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"testing"
        )
        
        func TestResolveSourcePrivacy(t *testing.T) {
        	tests := []struct {
        		name                string
        		roots               []string
        		edges               []annotated
        		expectedResolutions []res
        	}{
        		{
        			name:  "firstparty",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "notice",
        			roots: []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "lgpl",
        			roots: []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "proprietaryonresricted",
        			roots: []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        			},
        		},
        		{
        			name:  "restrictedonproprietary",
        			roots: []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        			actualRs := ResolveSourcePrivacy(lg)
        			checkResolves(actualRs, expectedRs, t)
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolveshare.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        // ResolveSourceSharing implements the policy for source-sharing.
        func ResolveSourceSharing(lg *LicenseGraph) ResolutionSet {
        	ResolveTopDownConditions(lg)
        	return WalkResolutionsForCondition(lg, ImpliesShared)
        }
        
        
        ================================================
        FILE: tools/compliance/policy_resolveshare_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"testing"
        )
        
        func TestResolveSourceSharing(t *testing.T) {
        	tests := []struct {
        		name                string
        		roots               []string
        		edges               []annotated
        		expectedResolutions []res
        	}{
        		{
        			name:  "independentmodulerestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "independentmodulerestrictedshipped",
        			roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "independentmodulestaticrestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "dependentmodulerestricted",
        			roots: []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "dependentmodulerestrictedshipclasspath",
        			roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "lgplonfprestricted",
        			roots: []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "lgplonfpdynamicrestricted",
        			roots: []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "lgplonfpdynamicrestrictedshiplib",
        			roots: []string{"lgplBin.meta_lic", "apacheLib.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:  "gplonfprestricted",
        			roots: []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "gplcontainerrestricted",
        			roots: []string{"gplContainer.meta_lic"},
        			edges: []annotated{
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "gploncontainerrestricted",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "restricted"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "gplonbinrestricted",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "gplonfpdynamicrestricted",
        			roots: []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "gplonfpdynamicrestrictedshiplib",
        			roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "independentmodulereverserestricted",
        			roots: []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "independentmodulereversestaticrestricted",
        			roots: []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "dependentmodulereverserestricted",
        			roots: []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "dependentmodulereverserestrictedshipdependent",
        			roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "ponrrestricted",
        			roots: []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"proprietary.meta_lic", "proprietary.meta_lic", "restricted"},
        				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "ronprestricted",
        			roots: []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "proprietary.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:  "noticeonb_e_orestricted",
        			roots: []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "b_e_oonnoticerestricted",
        			roots: []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:  "noticeonreciprecip",
        			roots: []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "mplLib.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:  "reciponnoticerecip",
        			roots: []string{"mplBin.meta_lic"},
        			edges: []annotated{
        				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        			actualRs := ResolveSourceSharing(lg)
        			checkResolves(actualRs, expectedRs, t)
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_shareprivacyconflicts.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        )
        
        // SourceSharePrivacyConflict describes an individual conflict between a source-sharing
        // condition and a source privacy condition
        type SourceSharePrivacyConflict struct {
        	SourceNode       *TargetNode
        	ShareCondition   LicenseCondition
        	PrivacyCondition LicenseCondition
        }
        
        // Error returns a string describing the conflict.
        func (conflict SourceSharePrivacyConflict) Error() string {
        	return fmt.Sprintf("%s %s and must share from %s condition\n", conflict.SourceNode.name,
        		conflict.PrivacyCondition.Name(), conflict.ShareCondition.Name())
        }
        
        // IsEqualTo returns true when `conflict` and `other` describe the same conflict.
        func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
        	return conflict.SourceNode.name == other.SourceNode.name &&
        		conflict.ShareCondition == other.ShareCondition &&
        		conflict.PrivacyCondition == other.PrivacyCondition
        }
        
        // ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
        // share the source and to keep the source private apply to the target.
        func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
        
        	ResolveTopDownConditions(lg)
        	// combined is the combination of source-sharing and source privacy.
        	combined := WalkActionsForCondition(lg, ImpliesShared.Union(ImpliesPrivate))
        
        	// size is the size of the result
        	size := 0
        	for actsOn, cs := range combined {
        		if actsOn.pure && !actsOn.LicenseConditions().MatchesAnySet(ImpliesShared) {
        			// no need to share code to build "a distribution medium"
        			continue
        		}
        		size += cs.Intersection(ImpliesShared).Len() * cs.Intersection(ImpliesPrivate).Len()
        	}
        	if size == 0 {
        		return nil
        	}
        	result := make([]SourceSharePrivacyConflict, 0, size)
        	for actsOn, cs := range combined {
        		if actsOn.pure { // no need to share code for "a distribution medium"
        			continue
        		}
        		pconditions := cs.Intersection(ImpliesPrivate).AsList()
        		ssconditions := cs.Intersection(ImpliesShared).AsList()
        
        		// report all conflicting condition combinations
        		for _, p := range pconditions {
        			for _, ss := range ssconditions {
        				result = append(result, SourceSharePrivacyConflict{actsOn, ss, p})
        			}
        		}
        	}
        	return result
        }
        
        
        ================================================
        FILE: tools/compliance/policy_shareprivacyconflicts_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"sort"
        	"testing"
        )
        
        // byConflict orders conflicts by target then share then privacy
        type byConflict []SourceSharePrivacyConflict
        
        // Len returns the count of elements in the slice.
        func (l byConflict) Len() int { return len(l) }
        
        // Swap rearranged 2 elements so that each occupies the other's former
        // position.
        func (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographically less than
        // the `j`th element.
        func (l byConflict) Less(i, j int) bool {
        	if l[i].SourceNode.Name() == l[j].SourceNode.Name() {
        		if l[i].ShareCondition.Name() == l[j].ShareCondition.Name() {
        			return l[i].PrivacyCondition.Name() < l[j].PrivacyCondition.Name()
        		}
        		return l[i].ShareCondition.Name() < l[j].ShareCondition.Name()
        	}
        	return l[i].SourceNode.Name() < l[j].SourceNode.Name()
        }
        
        func TestConflictingSharedPrivateSource(t *testing.T) {
        	tests := []struct {
        		name              string
        		roots             []string
        		edges             []annotated
        		expectedConflicts []confl
        	}{
        		{
        			name:  "firstparty",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedConflicts: []confl{},
        		},
        		{
        			name:  "notice",
        			roots: []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedConflicts: []confl{},
        		},
        		{
        			name:  "lgpl",
        			roots: []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedConflicts: []confl{},
        		},
        		{
        			name:  "proprietaryonrestricted",
        			roots: []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedConflicts: []confl{
        				{"proprietary.meta_lic", "gplLib.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
        			},
        		},
        		{
        			name:  "restrictedonproprietary",
        			roots: []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedConflicts: []confl{
        				{"proprietary.meta_lic", "gplBin.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedConflicts := toConflictList(lg, tt.expectedConflicts)
        			actualConflicts := ConflictingSharedPrivateSource(lg)
        			sort.Sort(byConflict(expectedConflicts))
        			sort.Sort(byConflict(actualConflicts))
        			if len(expectedConflicts) != len(actualConflicts) {
        				t.Errorf("unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts",
        					actualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))
        			} else {
        				for i := 0; i < len(actualConflicts); i++ {
        					if !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {
        						t.Errorf("unexpected share/privacy conflict at element %d: got %q, want %q",
        							i, actualConflicts[i].Error(), expectedConflicts[i].Error())
        					}
        				}
        			}
        
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_shipped.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        // ShippedNodes returns the set of nodes in a license graph where the target or
        // a derivative work gets distributed. (caches result)
        func ShippedNodes(lg *LicenseGraph) TargetNodeSet {
        	lg.mu.Lock()
        	shipped := lg.shippedNodes
        	lg.mu.Unlock()
        	if shipped != nil {
        		return *shipped
        	}
        
        	tset := make(TargetNodeSet)
        
        	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        		if _, alreadyWalked := tset[tn]; alreadyWalked {
        			return false
        		}
        		if len(path) > 0 {
        			if !edgeIsDerivation(path[len(path)-1].edge) {
        				return false
        			}
        		}
        		tset[tn] = struct{}{}
        		return true
        	})
        
        	shipped = &tset
        
        	lg.mu.Lock()
        	if lg.shippedNodes == nil {
        		lg.shippedNodes = shipped
        	} else {
        		// if we end up with 2, release the later for garbage collection.
        		shipped = lg.shippedNodes
        	}
        	lg.mu.Unlock()
        
        	return *shipped
        }
        
        
        ================================================
        FILE: tools/compliance/policy_shipped_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"sort"
        	"strings"
        	"testing"
        )
        
        func TestShippedNodes(t *testing.T) {
        	tests := []struct {
        		name          string
        		roots         []string
        		edges         []annotated
        		expectedNodes []string
        	}{
        		{
        			name:          "singleton",
        			roots:         []string{"apacheLib.meta_lic"},
        			edges:         []annotated{},
        			expectedNodes: []string{"apacheLib.meta_lic"},
        		},
        		{
        			name:  "simplebinary",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedNodes: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
        		},
        		{
        			name:  "simpledynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedNodes: []string{"apacheBin.meta_lic"},
        		},
        		{
        			name:  "container",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedNodes: []string{
        				"apacheContainer.meta_lic",
        				"apacheLib.meta_lic",
        				"gplLib.meta_lic",
        			},
        		},
        		{
        			name:  "binary",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedNodes: []string{
        				"apacheBin.meta_lic",
        				"apacheLib.meta_lic",
        				"gplLib.meta_lic",
        			},
        		},
        		{
        			name:  "binarydynamic",
        			roots: []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedNodes: []string{
        				"apacheBin.meta_lic",
        				"apacheLib.meta_lic",
        			},
        		},
        		{
        			name:  "containerdeep",
        			roots: []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheLib.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedNodes: []string{
        				"apacheContainer.meta_lic",
        				"apacheBin.meta_lic",
        				"apacheLib.meta_lic",
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			t.Logf("graph:")
        			for _, edge := range lg.Edges() {
        				t.Logf("  %s", edge.String())
        			}
        			expectedNodes := append([]string{}, tt.expectedNodes...)
        			nodeset := ShippedNodes(lg)
        			t.Logf("shipped node set: %s", nodeset.String())
        
        			actualNodes := nodeset.Names()
        			t.Logf("shipped nodes: [%s]", strings.Join(actualNodes, ", "))
        
        			sort.Strings(expectedNodes)
        			sort.Strings(actualNodes)
        
        			t.Logf("sorted nodes: [%s]", strings.Join(actualNodes, ", "))
        			t.Logf("expected nodes: [%s]", strings.Join(expectedNodes, ", "))
        			if len(expectedNodes) != len(actualNodes) {
        				t.Errorf("unexpected number of shipped nodes: %d nodes, want %d nodes",
        					len(actualNodes), len(expectedNodes))
        				return
        			}
        			for i := 0; i < len(actualNodes); i++ {
        				if expectedNodes[i] != actualNodes[i] {
        					t.Errorf("unexpected node at index %d: got %q, want %q",
        						i, actualNodes[i], expectedNodes[i])
        				}
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/policy_walk.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        // EdgeContextProvider is an interface for injecting edge-specific context
        // into walk paths.
        type EdgeContextProvider interface {
        	// Context returns the context for `edge` when added to `path`.
        	Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{}
        }
        
        // NoEdgeContext implements EdgeContextProvider for walks that use no context.
        type NoEdgeContext struct{}
        
        // Context returns nil.
        func (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
        	return nil
        }
        
        // ApplicableConditionsContext provides the subset of conditions in `universe`
        // that apply to each edge in a path.
        type ApplicableConditionsContext struct {
        	universe LicenseConditionSet
        }
        
        // Context returns the LicenseConditionSet applicable to the edge.
        func (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
        	universe := ctx.universe
        	if len(path) > 0 {
        		universe = path[len(path)-1].ctx.(LicenseConditionSet)
        	}
        	return conditionsAttachingAcrossEdge(lg, edge, universe)
        }
        
        // VisitNode is called for each root and for each walked dependency node by
        // WalkTopDown and WalkTopDownBreadthFirst. When VisitNode returns true, WalkTopDown will proceed to walk
        // down the dependences of the node
        type VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool
        
        // WalkTopDown does a top-down walk of `lg` calling `visit` and descending
        // into depenencies when `visit` returns true.
        func WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {
        	path := NewTargetEdgePath(32)
        
        	var walk func(fnode *TargetNode)
        	walk = func(fnode *TargetNode) {
        		visitChildren := visit(lg, fnode, *path)
        		if !visitChildren {
        			return
        		}
        		for _, edge := range fnode.edges {
        			var edgeContext interface{}
        			if ctx == nil {
        				edgeContext = nil
        			} else {
        				edgeContext = ctx.Context(lg, *path, edge)
        			}
        			path.Push(edge, edgeContext)
        			walk(edge.dependency)
        			path.Pop()
        		}
        	}
        
        	for _, r := range lg.rootFiles {
        		path.Clear()
        		walk(lg.targets[r])
        	}
        }
        
        // WalkTopDownBreadthFirst performs a Breadth-first top down walk of `lg` calling `visit` and descending
        // into depenencies when `visit` returns true.
        func WalkTopDownBreadthFirst(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {
        	path := NewTargetEdgePath(32)
        
        	var walk func(fnode *TargetNode)
        	walk = func(fnode *TargetNode) {
        		edgesToWalk := make(TargetEdgeList, 0, len(fnode.edges))
        		for _, edge := range fnode.edges {
        			var edgeContext interface{}
        			if ctx == nil {
        				edgeContext = nil
        			} else {
        				edgeContext = ctx.Context(lg, *path, edge)
        			}
        			path.Push(edge, edgeContext)
        			if visit(lg, edge.dependency, *path){
        				edgesToWalk = append(edgesToWalk, edge)
        			}
        			path.Pop()
        		}
        
        		for _, edge := range(edgesToWalk) {
        			var edgeContext interface{}
        			if ctx == nil {
        				edgeContext = nil
        			} else {
        				edgeContext = ctx.Context(lg, *path, edge)
        			}
        			path.Push(edge, edgeContext)
        			walk(edge.dependency)
        			path.Pop()
        		}
        	}
        
        	path.Clear()
        	rootsToWalk := make([]*TargetNode, 0, len(lg.rootFiles))
        	for _, r := range lg.rootFiles {
        		if visit(lg, lg.targets[r], *path){
        			rootsToWalk = append(rootsToWalk, lg.targets[r])
        		}
        	}
        
        	for _, rnode := range(rootsToWalk) {
        		walk(rnode)
        	}
        }
        
        // resolutionKey identifies results from walking a specific target for a
        // specific set of conditions.
        type resolutionKey struct {
        	target *TargetNode
        	cs     LicenseConditionSet
        }
        
        // WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
        // resolving all distributed works for `conditions`.
        func WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet {
        	shipped := ShippedNodes(lg)
        
        	// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
        	rmap := make(map[resolutionKey]ActionSet)
        
        	// cmap identifies previously walked target/condition pairs.
        	cmap := make(map[resolutionKey]struct{})
        
        	// result accumulates the resolutions to return.
        	result := make(ResolutionSet)
        	WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        		universe := conditions
        		if len(path) > 0 {
        			universe = path[len(path)-1].ctx.(LicenseConditionSet)
        		}
        
        		if universe.IsEmpty() {
        			return false
        		}
        		key := resolutionKey{tn, universe}
        
        		if _, alreadyWalked := cmap[key]; alreadyWalked {
        			pure := true
        			for _, p := range path {
        				target := p.Target()
        				tkey := resolutionKey{target, universe}
        				if _, ok := rmap[tkey]; !ok {
        					rmap[tkey] = make(ActionSet)
        				}
        				// attach prior walk outcome to ancestor
        				for actsOn, cs := range rmap[key] {
        					rmap[tkey][actsOn] = cs
        				}
        				// if prior walk produced results, copy results
        				// to ancestor.
        				if _, ok := result[tn]; ok && pure {
        					if _, ok := result[target]; !ok {
        						result[target] = make(ActionSet)
        					}
        					for actsOn, cs := range result[tn] {
        						result[target][actsOn] = cs
        					}
        					pure = target.IsContainer()
        				}
        			}
        			// if all ancestors are pure aggregates, attach
        			// matching prior walk conditions to self. Prior walk
        			// will not have done so if any ancestor was not an
        			// aggregate.
        			if pure {
        				match := rmap[key][tn].Intersection(universe)
        				if !match.IsEmpty() {
        					if _, ok := result[tn]; !ok {
        						result[tn] = make(ActionSet)
        					}
        					result[tn][tn] = match
        				}
        			}
        			return false
        		}
        		// no need to walk node or dependencies if not shipped
        		if !shipped.Contains(tn) {
        			return false
        		}
        		if _, ok := rmap[key]; !ok {
        			rmap[key] = make(ActionSet)
        		}
        		// add self to walk outcome
        		rmap[key][tn] = tn.resolution
        		cmap[key] = struct{}{}
        		cs := tn.resolution
        		if !cs.IsEmpty() {
        			cs = cs.Intersection(universe)
        			pure := true
        			for _, p := range path {
        				target := p.Target()
        				tkey := resolutionKey{target, universe}
        				if _, ok := rmap[tkey]; !ok {
        					rmap[tkey] = make(ActionSet)
        				}
        				// copy current node's action into ancestor
        				rmap[tkey][tn] = tn.resolution
        				// conditionally put matching conditions into
        				// result
        				if pure && !cs.IsEmpty() {
        					if _, ok := result[target]; !ok {
        						result[target] = make(ActionSet)
        					}
        					result[target][tn] = cs
        					pure = target.IsContainer()
        				}
        			}
        			// if all ancestors are pure aggregates, attach
        			// matching conditions to self.
        			if pure && !cs.IsEmpty() {
        				if _, ok := result[tn]; !ok {
        					result[tn] = make(ActionSet)
        				}
        				result[tn][tn] = cs
        			}
        		}
        		return true
        	})
        
        	return result
        }
        
        // WalkActionsForCondition performs a top-down walk of the LicenseGraph
        // resolving all distributed works for `conditions`.
        func WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet {
        	// amap maps 'actsOn' targets to the applicable conditions
        	//
        	// amap is the resulting ActionSet
        	amap := make(ActionSet)
        
        	for tn := range ShippedNodes(lg) {
        		if cs := conditions.Intersection(tn.resolution); !cs.IsEmpty() {
        			amap[tn] = cs
        		}
        	}
        
        	return amap
        }
        
        
        ================================================
        FILE: tools/compliance/policy_walk_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"fmt"
        	"os"
        	"strings"
        	"testing"
        )
        
        func TestMain(m *testing.M) {
        	// Change into the cmd directory before running the tests
        	// so they can find the testdata directory.
        	if err := os.Chdir("cmd"); err != nil {
        		fmt.Printf("failed to change to testdata directory: %s\n", err)
        		os.Exit(1)
        	}
        	os.Exit(m.Run())
        }
        
        func TestWalkResolutionsForCondition(t *testing.T) {
        	tests := []struct {
        		name                string
        		condition           LicenseConditionSet
        		roots               []string
        		edges               []annotated
        		expectedResolutions []res
        	}{
        		{
        			name:      "firstparty",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "notice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        				{"mitBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "fponlgplnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted_if_statically_linked"},
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "fponlgpldynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "independentmodulestaticnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "independentmodulestaticrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "dependentmodulenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "dependentmodulerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "lgplonfpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfpdynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfpdynamicrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "gplonfpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "apacheLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:      "gplonfprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplcontainernotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplContainer.meta_lic"},
        			edges: []annotated{
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", "notice|restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:      "gplcontainerrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplContainer.meta_lic"},
        			edges: []annotated{
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gploncontainernotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", "notice"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
        				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gploncontainerrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "restricted"},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonbinnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", "notice|restricted"},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonbinrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"apacheBin.meta_lic", "apacheBin.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicrestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "apacheLib.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "independentmodulereversenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "independentmodulereverserestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "independentmodulereverserestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "independentmodulereversestaticnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulereversestaticrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "dependentmodulereversenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{
        				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "dependentmodulereverserestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "dependentmodulereverserestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "ponrnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"proprietary.meta_lic", "proprietary.meta_lic", "restricted|proprietary"},
        				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ponrrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
        				{"proprietary.meta_lic", "proprietary.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ponrproprietary",
        			condition: ImpliesProprietary,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
        			},
        		},
        		{
        			name:      "ronpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "proprietary.meta_lic", "restricted|proprietary"},
        			},
        		},
        		{
        			name:      "ronprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
        				{"gplBin.meta_lic", "proprietary.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ronpproprietary",
        			condition: ImpliesProprietary,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary"},
        			},
        		},
        		{
        			name:      "noticeonb_e_onotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        				{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "noticeonb_e_orestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "noticeonb_e_ob_e_o",
        			condition: ImpliesByExceptionOnly,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "b_e_oonnoticenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        				{"by_exception.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "b_e_oonnoticerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{},
        		},
        		{
        			name:      "b_e_oonnoticeb_e_o",
        			condition: ImpliesByExceptionOnly,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "noticeonrecipnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
        				{"mitBin.meta_lic", "mplLib.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:      "noticeonreciprecip",
        			condition: ImpliesReciprocal,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mitBin.meta_lic", "mplLib.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:      "reciponnoticenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mplBin.meta_lic"},
        			edges: []annotated{
        				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        				{"mplBin.meta_lic", "mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "reciponnoticerecip",
        			condition: ImpliesReciprocal,
        			roots:     []string{"mplBin.meta_lic"},
        			edges: []annotated{
        				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedResolutions: []res{
        				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
        			ResolveTopDownConditions(lg)
        			actualRs := WalkResolutionsForCondition(lg, tt.condition)
        			checkResolves(actualRs, expectedRs, t)
        		})
        	}
        }
        
        func TestWalkActionsForCondition(t *testing.T) {
        	tests := []struct {
        		name            string
        		condition       LicenseConditionSet
        		roots           []string
        		edges           []annotated
        		expectedActions []act
        	}{
        		{
        			name:      "firstparty",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice"},
        				{"apacheLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "notice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mitBin.meta_lic", "notice"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "fponlgplnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "fponlgpldynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "independentmodulestaticnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice"},
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "independentmodulestaticrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "dependentmodulenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"dependentModule.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "dependentmodulerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"dependentModule.meta_lic"},
        			edges: []annotated{
        				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "lgplonfpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"apacheLib.meta_lic", "notice|restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"lgplBin.meta_lic", "restricted_if_statically_linked"},
        				{"apacheLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfpdynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "lgplonfpdynamicrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"lgplBin.meta_lic"},
        			edges: []annotated{
        				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"lgplBin.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        		{
        			name:      "gplonfpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:      "gplonfprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplcontainernotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplContainer.meta_lic"},
        			edges: []annotated{
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplContainer.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "notice|restricted"},
        			},
        		},
        		{
        			name:      "gplcontainerrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplContainer.meta_lic"},
        			edges: []annotated{
        				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplContainer.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gploncontainernotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheContainer.meta_lic", "notice|restricted"},
        				{"apacheLib.meta_lic", "notice"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gploncontainerrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheContainer.meta_lic"},
        			edges: []annotated{
        				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheContainer.meta_lic", "restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonbinnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "notice|restricted"},
        				{"apacheLib.meta_lic", "notice|restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonbinrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"apacheBin.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "restricted"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "gplonfpdynamicrestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        				{"apacheLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "independentmodulereversenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "independentmodulereverserestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "independentmodulereverserestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "independentmodulereversestaticnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplWithClasspathException.meta_lic", "permissive"},
        				{"apacheBin.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "independentmodulereversestaticrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "dependentmodulereversenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{
        				{"gplWithClasspathException.meta_lic", "permissive"},
        			},
        		},
        		{
        			name:      "dependentmodulereverserestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "dependentmodulereverserestrictedshipped",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
        			edges: []annotated{
        				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "ponrnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"proprietary.meta_lic", "restricted|proprietary"},
        				{"gplLib.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ponrrestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplLib.meta_lic", "restricted"},
        				{"proprietary.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ponrproprietary",
        			condition: ImpliesProprietary,
        			roots:     []string{"proprietary.meta_lic"},
        			edges: []annotated{
        				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"proprietary.meta_lic", "proprietary"},
        			},
        		},
        		{
        			name:      "ronpnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        				{"proprietary.meta_lic", "restricted|proprietary"},
        			},
        		},
        		{
        			name:      "ronprestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"gplBin.meta_lic", "restricted"},
        				{"proprietary.meta_lic", "restricted"},
        			},
        		},
        		{
        			name:      "ronpproprietary",
        			condition: ImpliesProprietary,
        			roots:     []string{"gplBin.meta_lic"},
        			edges: []annotated{
        				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"proprietary.meta_lic", "proprietary"},
        			},
        		},
        		{
        			name:      "noticeonb_e_onotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mitBin.meta_lic", "notice"},
        				{"by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "noticeonb_e_orestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "noticeonb_e_ob_e_o",
        			condition: ImpliesByExceptionOnly,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "b_e_oonnoticenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"by_exception.meta_lic", "by_exception_only"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "b_e_oonnoticerestricted",
        			condition: ImpliesRestricted,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{},
        		},
        		{
        			name:      "b_e_oonnoticeb_e_o",
        			condition: ImpliesByExceptionOnly,
        			roots:     []string{"by_exception.meta_lic"},
        			edges: []annotated{
        				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"by_exception.meta_lic", "by_exception_only"},
        			},
        		},
        		{
        			name:      "noticeonrecipnotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mitBin.meta_lic", "notice"},
        				{"mplLib.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:      "noticeonreciprecip",
        			condition: ImpliesReciprocal,
        			roots:     []string{"mitBin.meta_lic"},
        			edges: []annotated{
        				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mplLib.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:      "reciponnoticenotice",
        			condition: ImpliesNotice,
        			roots:     []string{"mplBin.meta_lic"},
        			edges: []annotated{
        				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mplBin.meta_lic", "reciprocal"},
        				{"mitLib.meta_lic", "notice"},
        			},
        		},
        		{
        			name:      "reciponnoticerecip",
        			condition: ImpliesReciprocal,
        			roots:     []string{"mplBin.meta_lic"},
        			edges: []annotated{
        				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"mplBin.meta_lic", "reciprocal"},
        			},
        		},
        		{
        			name:      "regress-walk-twice",
        			condition: ImpliesShared,
        			roots:     []string{"mitBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic"},
        			edges: []annotated{
        				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
        				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
        				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
        				{"mitBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
        			},
        			expectedActions: []act{
        				{"apacheBin.meta_lic", "restricted"},
        				{"mitLib.meta_lic", "restricted|restricted_if_statically_linked"},
        				{"gplLib.meta_lic", "restricted"},
        				{"mitBin.meta_lic", "restricted_if_statically_linked"},
        				{"lgplLib.meta_lic", "restricted_if_statically_linked"},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := toGraph(stderr, tt.roots, tt.edges)
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        			expectedAs := toActionSet(lg, tt.expectedActions)
        			ResolveTopDownConditions(lg)
        			actualAs := WalkActionsForCondition(lg, tt.condition)
        			checkResolvesActions(lg, actualAs, expectedAs, t)
        		})
        	}
        }
        
        func TestWalkTopDownBreadthFirst(t *testing.T) {
        	tests := []struct {
        		name           string
        		roots          []string
        		edges          []annotated
        		expectedResult []string
        	}{
        		{
        			name:  "bin/bin1",
        			roots: []string{"bin/bin1.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin2",
        			roots: []string{"bin/bin2.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin3",
        			roots: []string{"bin/bin3.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin3.meta_lic",
        			},
        		},
        		{
        			name:  "lib/liba.so",
        			roots: []string{"lib/liba.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/liba.so.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libb.so",
        			roots: []string{"lib/libb.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libc.so",
        			roots: []string{"lib/libc.a.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libd.so",
        			roots: []string{"lib/libd.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "highest.apex",
        			roots: []string{"highest.apex.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "container.zip",
        			roots: []string{"container.zip.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "application",
        			roots: []string{"application.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin1&lib/liba",
        			roots: []string{"bin/bin1.meta_lic","lib/liba.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin2&lib/libd",
        			roots: []string{"bin/bin2.meta_lic", "lib/libd.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "application&bin/bin3",
        			roots: []string{"application.meta_lic", "bin/bin3.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "highest.apex&container.zip",
        			roots: []string{"highest.apex.meta_lic", "container.zip.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        	}
        
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			actualOut := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/notice/"+r)
        			}
        
        			lg, err := ReadLicenseGraph(GetFS(""), stderr, rootFiles)
        
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        
        			expectedRst := tt.expectedResult
        
        			WalkTopDownBreadthFirst(nil, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        				fmt.Fprintln(actualOut, tn.Name())
        				return true
        			})
        
        			actualRst := strings.Split(actualOut.String(), "\n")
        
        			if len(actualRst) > 0 {
        				actualRst = actualRst[:len(actualRst)-1]
        			}
        
        			t.Logf("actual nodes visited: %s", actualOut.String())
        			t.Logf("expected nodes visited: %s", strings.Join(expectedRst, "\n"))
        
        			if len(actualRst) != len(expectedRst) {
        				t.Errorf("WalkTopDownBreadthFirst: number of visited nodes is different: got %d, want %d", len(actualRst), len(expectedRst))
        			}
        
        			for i := 0; i < len(actualRst) && i < len(expectedRst); i++ {
        				if actualRst[i] != expectedRst[i] {
        					t.Errorf("WalkTopDownBreadthFirst: lines differ at index %d: got %q, want %q", i, actualRst[i], expectedRst[i])
        					break
        				}
        			}
        
        			if len(actualRst) < len(expectedRst) {
        				t.Errorf("WalkTopDownBreadthFirst: extra lines at %d: got %q, want nothing", len(actualRst), expectedRst[len(actualRst)])
        			}
        
        			if len(expectedRst) < len(actualRst) {
        				t.Errorf("WalkTopDownBreadthFirst: missing lines at %d: got nothing, want %q", len(expectedRst), actualRst[len(expectedRst)])
        			}
        		})
        	}
        }
        
        func TestWalkTopDownBreadthFirstWithoutDuplicates(t *testing.T) {
        	tests := []struct {
        		name           string
        		roots          []string
        		edges          []annotated
        		expectedResult []string
        	}{
        		{
        			name:  "bin/bin1",
        			roots: []string{"bin/bin1.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin2",
        			roots: []string{"bin/bin2.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin3",
        			roots: []string{"bin/bin3.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin3.meta_lic",
        			},
        		},
        		{
        			name:  "lib/liba.so",
        			roots: []string{"lib/liba.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/liba.so.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libb.so",
        			roots: []string{"lib/libb.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libc.so",
        			roots: []string{"lib/libc.a.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "lib/libd.so",
        			roots: []string{"lib/libd.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "highest.apex",
        			roots: []string{"highest.apex.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "container.zip",
        			roots: []string{"container.zip.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        		{
        			name:  "application",
        			roots: []string{"application.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin1&lib/liba",
        			roots: []string{"bin/bin1.meta_lic", "lib/liba.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        			},
        		},
        		{
        			name:  "bin/bin2&lib/libd",
        			roots: []string{"bin/bin2.meta_lic", "lib/libd.so.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "application&bin/bin3",
        			roots: []string{"application.meta_lic", "bin/bin3.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/application.meta_lic",
        				"testdata/notice/bin/bin3.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        			},
        		},
        		{
        			name:  "highest.apex&container.zip",
        			roots: []string{"highest.apex.meta_lic", "container.zip.meta_lic"},
        			expectedResult: []string{
        				"testdata/notice/highest.apex.meta_lic",
        				"testdata/notice/container.zip.meta_lic",
        				"testdata/notice/bin/bin1.meta_lic",
        				"testdata/notice/bin/bin2.meta_lic",
        				"testdata/notice/lib/liba.so.meta_lic",
        				"testdata/notice/lib/libb.so.meta_lic",
        				"testdata/notice/lib/libc.a.meta_lic",
        				"testdata/notice/lib/libd.so.meta_lic",
        			},
        		},
        	}
        
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			actualOut := &bytes.Buffer{}
        
        			rootFiles := make([]string, 0, len(tt.roots))
        			for _, r := range tt.roots {
        				rootFiles = append(rootFiles, "testdata/notice/"+r)
        			}
        
        			lg, err := ReadLicenseGraph(GetFS(""), stderr, rootFiles)
        
        			if err != nil {
        				t.Errorf("unexpected test data error: got %s, want no error", err)
        				return
        			}
        
        			expectedRst := tt.expectedResult
        
        			//Keeping track of the visited nodes
        			//Only add to actualOut if not visited
        			visitedNodes := make(map[string]struct{})
        			WalkTopDownBreadthFirst(nil, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
        				if _, alreadyVisited := visitedNodes[tn.Name()]; alreadyVisited {
        					return false
        				}
        				fmt.Fprintln(actualOut, tn.Name())
        				visitedNodes[tn.Name()] = struct{}{}
        				return true
        			})
        
        			actualRst := strings.Split(actualOut.String(), "\n")
        
        			if len(actualRst) > 0 {
        				actualRst = actualRst[:len(actualRst)-1]
        			}
        
        			t.Logf("actual nodes visited: %s", actualOut.String())
        			t.Logf("expected nodes visited: %s", strings.Join(expectedRst, "\n"))
        
        			if len(actualRst) != len(expectedRst) {
        				t.Errorf("WalkTopDownBreadthFirst: number of visited nodes is different: got %d, want %d", len(actualRst), len(expectedRst))
        			}
        
        			for i := 0; i < len(actualRst) && i < len(expectedRst); i++ {
        				if actualRst[i] != expectedRst[i] {
        					t.Errorf("WalkTopDownBreadthFirst: lines differ at index %d: got %q, want %q", i, actualRst[i], expectedRst[i])
        					break
        				}
        			}
        
        			if len(actualRst) < len(expectedRst) {
        				t.Errorf("WalkTopDownBreadthFirst: extra lines at %d: got %q, want nothing", len(actualRst), expectedRst[len(actualRst)])
        			}
        
        			if len(expectedRst) < len(actualRst) {
        				t.Errorf("WalkTopDownBreadthFirst: missing lines at %d: got nothing, want %q", len(expectedRst), actualRst[len(expectedRst)])
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/projectmetadata/Android.bp
        ================================================
        // Copyright (C) 2022 The Android Open Source Project
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package {
            default_applicable_licenses: ["Android-Apache-2.0"],
        }
        
        bootstrap_go_package {
            name: "projectmetadata-module",
            srcs: [
                "projectmetadata.go",
            ],
            deps: [
                "compliance-test-fs-module",
                "golang-protobuf-proto",
                "golang-protobuf-encoding-prototext",
                "project_metadata_proto",
            ],
            testSrcs: [
                "projectmetadata_test.go",
            ],
            pkgPath: "android/soong/tools/compliance/projectmetadata",
        }
        
        
        ================================================
        FILE: tools/compliance/projectmetadata/projectmetadata.go
        ================================================
        // Copyright 2022 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package projectmetadata
        
        import (
        	"fmt"
        	"io"
        	"io/fs"
        	"path/filepath"
        	"strings"
        	"sync"
        
        	"android/soong/compliance/project_metadata_proto"
        
        	"google.golang.org/protobuf/encoding/prototext"
        )
        
        var (
        	// ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.
        	ConcurrentReaders = 5
        )
        
        // ProjectMetadata contains the METADATA for a git project.
        type ProjectMetadata struct {
        	proto project_metadata_proto.Metadata
        
        	// project is the path to the directory containing the METADATA file.
        	project string
        }
        
        // ProjectUrlMap maps url type name to url value
        type ProjectUrlMap map[string]string
        
        // DownloadUrl returns the address of a download location
        func (m ProjectUrlMap) DownloadUrl() string {
        	for _, urlType := range []string{"GIT", "SVN", "HG", "DARCS"} {
        		if url, ok := m[urlType]; ok {
        			return url
        		}
        	}
        	return ""
        }
        
        // String returns a string representation of the metadata for error messages.
        func (pm *ProjectMetadata) String() string {
        	return fmt.Sprintf("project: %q\n%s", pm.project, pm.proto.String())
        }
        
        // Project returns the path to the directory containing the METADATA file
        func (pm *ProjectMetadata) Project() string {
        	return pm.project
        }
        
        // Name returns the name of the project.
        func (pm *ProjectMetadata) Name() string {
        	return pm.proto.GetName()
        }
        
        // Version returns the version of the project if available.
        func (pm *ProjectMetadata) Version() string {
        	tp := pm.proto.GetThirdParty()
        	if tp != nil {
        		version := tp.GetVersion()
        		return version
        	}
        	return ""
        }
        
        // VersionedName returns the name of the project including the version if any.
        func (pm *ProjectMetadata) VersionedName() string {
        	name := pm.proto.GetName()
        	if name != "" {
        		tp := pm.proto.GetThirdParty()
        		if tp != nil {
        			version := tp.GetVersion()
        			if version != "" {
        				if version[0] == 'v' || version[0] == 'V' {
        					return name + "_" + version
        				} else {
        					return name + "_v_" + version
        				}
        			}
        		}
        		return name
        	}
        	return pm.proto.GetDescription()
        }
        
        // UrlsByTypeName returns a map of URLs by Type Name
        func (pm *ProjectMetadata) UrlsByTypeName() ProjectUrlMap {
        	tp := pm.proto.GetThirdParty()
        	if tp == nil {
        		return nil
        	}
        	if len(tp.Url) == 0 {
        		return nil
        	}
        	urls := make(ProjectUrlMap)
        
        	for _, url := range tp.Url {
        		uri := url.GetValue()
        		if uri == "" {
        			continue
        		}
        		urls[project_metadata_proto.URL_Type_name[int32(url.GetType())]] = uri
        	}
        	return urls
        }
        
        // projectIndex describes a project to be read; after `wg.Wait()`, will contain either
        // a `ProjectMetadata`, pm (can be nil even without error), or a non-nil `err`.
        type projectIndex struct {
        	project string
        	path    string
        	pm      *ProjectMetadata
        	err     error
        	done    chan struct{}
        }
        
        // finish marks the task to read the `projectIndex` completed.
        func (pi *projectIndex) finish() {
        	close(pi.done)
        }
        
        // wait suspends execution until the `projectIndex` task completes.
        func (pi *projectIndex) wait() {
        	<-pi.done
        }
        
        // Index reads and caches ProjectMetadata (thread safe)
        type Index struct {
        	// projecs maps project name to a wait group if read has already started, and
        	// to a `ProjectMetadata` or to an `error` after the read completes.
        	projects sync.Map
        
        	// task provides a fixed-size task pool to limit concurrent open files etc.
        	task chan bool
        
        	// rootFS locates the root of the file system from which to read the files.
        	rootFS fs.FS
        }
        
        // NewIndex constructs a project metadata `Index` for the given file system.
        func NewIndex(rootFS fs.FS) *Index {
        	ix := &Index{task: make(chan bool, ConcurrentReaders), rootFS: rootFS}
        	for i := 0; i < ConcurrentReaders; i++ {
        		ix.task <- true
        	}
        	return ix
        }
        
        // MetadataForProjects returns 0..n ProjectMetadata for n `projects`, or an error.
        // Each project that has a METADATA.android or a METADATA file in the root of the project will have
        // a corresponding ProjectMetadata in the result. Projects with neither file get skipped. A nil
        // result with no error indicates none of the given `projects` has a METADATA file.
        // (thread safe -- can be called concurrently from multiple goroutines)
        func (ix *Index) MetadataForProjects(projects ...string) ([]*ProjectMetadata, error) {
        	if ConcurrentReaders < 1 {
        		return nil, fmt.Errorf("need at least one task in project metadata pool")
        	}
        	if len(projects) == 0 {
        		return nil, nil
        	}
        	// Identify the projects that have never been read
        	projectsToRead := make([]*projectIndex, 0, len(projects))
        	projectIndexes := make([]*projectIndex, 0, len(projects))
        	for _, p := range projects {
        		pi, loaded := ix.projects.LoadOrStore(p, &projectIndex{project: p, done: make(chan struct{})})
        		if !loaded {
        			projectsToRead = append(projectsToRead, pi.(*projectIndex))
        		}
        		projectIndexes = append(projectIndexes, pi.(*projectIndex))
        	}
        	// findMeta locates and reads the appropriate METADATA file, if any.
        	findMeta := func(pi *projectIndex) {
        		<-ix.task
        		defer func() {
        			ix.task <- true
        			pi.finish()
        		}()
        
        		// Support METADATA.android for projects that already have a different sort of METADATA file.
        		path := filepath.Join(pi.project, "METADATA.android")
        		fi, err := fs.Stat(ix.rootFS, path)
        		if err == nil {
        			if fi.Mode().IsRegular() {
        				ix.readMetadataFile(pi, path)
        				return
        			}
        		}
        		// No METADATA.android try METADATA file.
        		path = filepath.Join(pi.project, "METADATA")
        		fi, err = fs.Stat(ix.rootFS, path)
        		if err == nil {
        			if fi.Mode().IsRegular() {
        				ix.readMetadataFile(pi, path)
        				return
        			}
        		}
        		// no METADATA file exists -- leave nil and finish
        	}
        	// Look for the METADATA files to read, and record any missing.
        	for _, p := range projectsToRead {
        		go findMeta(p)
        	}
        	// Wait until all of the projects have been read.
        	var msg strings.Builder
        	result := make([]*ProjectMetadata, 0, len(projects))
        	for _, pi := range projectIndexes {
        		pi.wait()
        		// Combine any errors into a single error.
        		if pi.err != nil {
        			fmt.Fprintf(&msg, "  %v\n", pi.err)
        		} else if pi.pm != nil {
        			result = append(result, pi.pm)
        		}
        	}
        	if msg.Len() > 0 {
        		return nil, fmt.Errorf("error reading project(s):\n%s", msg.String())
        	}
        	if len(result) == 0 {
        		return nil, nil
        	}
        	return result, nil
        }
        
        // AllMetadataFiles returns the sorted list of all METADATA files read thus far.
        func (ix *Index) AllMetadataFiles() []string {
        	var files []string
        	ix.projects.Range(func(key, value any) bool {
        		pi := value.(*projectIndex)
        		if pi.path != "" {
        			files = append(files, pi.path)
        		}
        		return true
        	})
        	return files
        }
        
        // readMetadataFile tries to read and parse a METADATA file at `path` for `project`.
        func (ix *Index) readMetadataFile(pi *projectIndex, path string) {
        	f, err := ix.rootFS.Open(path)
        	if err != nil {
        		pi.err = fmt.Errorf("error opening project %q metadata %q: %w", pi.project, path, err)
        		return
        	}
        
        	// read the file
        	data, err := io.ReadAll(f)
        	if err != nil {
        		pi.err = fmt.Errorf("error reading project %q metadata %q: %w", pi.project, path, err)
        		return
        	}
        	f.Close()
        
        	uo := prototext.UnmarshalOptions{DiscardUnknown: true}
        	pm := &ProjectMetadata{project: pi.project}
        	err = uo.Unmarshal(data, &pm.proto)
        	if err != nil {
        		pi.err = fmt.Errorf(`error in project %q METADATA %q: %v
        
        METADATA and METADATA.android files must parse as text protobufs
        defined by
           build/soong/compliance/project_metadata_proto/project_metadata.proto
        
        * unknown fields don't matter
        * check invalid ENUM names
        * check quoting
        * check unescaped nested quotes
        * check the comment marker for protobuf is '#' not '//'
        
        if importing a library that uses a different sort of METADATA file, add
        a METADATA.android file beside it to parse instead
        `, pi.project, path, err)
        		return
        	}
        
        	pi.path = path
        	pi.pm = pm
        }
        
        
        ================================================
        FILE: tools/compliance/projectmetadata/projectmetadata_test.go
        ================================================
        // Copyright 2022 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package projectmetadata
        
        import (
        	"fmt"
        	"strings"
        	"testing"
        
        	"android/soong/compliance/project_metadata_proto"
        	"android/soong/tools/compliance/testfs"
        )
        
        const (
        	// EMPTY represents a METADATA file with no recognized fields
        	EMPTY = ``
        
        	// INVALID_NAME represents a METADATA file with the wrong type of name
        	INVALID_NAME = `name: a library\n`
        
        	// INVALID_DESCRIPTION represents a METADATA file with the wrong type of description
        	INVALID_DESCRIPTION = `description: unquoted text\n`
        
        	// INVALID_VERSION represents a METADATA file with the wrong type of version
        	INVALID_VERSION = `third_party { version: 1 }`
        
        	// MY_LIB_1_0 represents a METADATA file for version 1.0 of mylib
        	MY_LIB_1_0 = `name: "mylib" description: "my library" third_party { version: "1.0" }`
        
        	// NO_NAME_0_1 represents a METADATA file with a description but no name
        	NO_NAME_0_1 = `description: "my library" third_party { version: "0.1" }`
        
        	// URL values per type
        	GIT_URL          = "http://example.github.com/my_lib"
        	SVN_URL          = "http://example.svn.com/my_lib"
        	HG_URL           = "http://example.hg.com/my_lib"
        	DARCS_URL        = "http://example.darcs.com/my_lib"
        	PIPER_URL        = "http://google3/third_party/my/package"
        	HOMEPAGE_URL     = "http://example.com/homepage"
        	OTHER_URL        = "http://google.com/"
        	ARCHIVE_URL      = "http://ftp.example.com/"
        	LOCAL_SOURCE_URL = "https://android.googlesource.com/platform/external/apache-http/"
        )
        
        // libWithUrl returns a METADATA file with the right download url
        func libWithUrl(urlTypes ...string) string {
        	var sb strings.Builder
        
        	fmt.Fprintln(&sb, `name: "mylib" description: "my library"
        	 third_party {
        	 	version: "1.0"`)
        
        	for _, urltype := range urlTypes {
        		var urlValue string
        		switch urltype {
        		case "GIT":
        			urlValue = GIT_URL
        		case "SVN":
        			urlValue = SVN_URL
        		case "HG":
        			urlValue = HG_URL
        		case "DARCS":
        			urlValue = DARCS_URL
        		case "PIPER":
        			urlValue = PIPER_URL
        		case "HOMEPAGE":
        			urlValue = HOMEPAGE_URL
        		case "OTHER":
        			urlValue = OTHER_URL
        		case "ARCHIVE":
        			urlValue = ARCHIVE_URL
        		case "LOCAL_SOURCE":
        			urlValue = LOCAL_SOURCE_URL
        		default:
        			panic(fmt.Errorf("unknown url type: %q. Please update libWithUrl() in build/make/tools/compliance/projectmetadata/projectmetadata_test.go", urltype))
        		}
        		fmt.Fprintf(&sb, "  url { type: %s value: %q }\n", urltype, urlValue)
        	}
        	fmt.Fprintln(&sb, `}`)
        
        	return sb.String()
        }
        
        func TestVerifyAllUrlTypes(t *testing.T) {
        	t.Run("verifyAllUrlTypes", func(t *testing.T) {
        		types := make([]string, 0, len(project_metadata_proto.URL_Type_value))
        		for t := range project_metadata_proto.URL_Type_value {
        			types = append(types, t)
        		}
        		libWithUrl(types...)
        	})
        }
        
        func TestUnknownPanics(t *testing.T) {
        	t.Run("Unknown panics", func(t *testing.T) {
        		defer func() {
        			if r := recover(); r == nil {
        				t.Errorf("unexpected success: got no error, want panic")
        			}
        		}()
        		libWithUrl("SOME WILD VALUE THAT DOES NOT EXIST")
        	})
        }
        
        func TestReadMetadataForProjects(t *testing.T) {
        	tests := []struct {
        		name          string
        		fs            *testfs.TestFS
        		projects      []string
        		expectedError string
        		expected      []pmeta
        	}{
        		{
        			name: "trivial",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte("name: \"Android\"\n"),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "Android",
        				name:          "Android",
        				version:       "",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "versioned",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(MY_LIB_1_0),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_homepage",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("HOMEPAGE")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_git",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("GIT")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   GIT_URL,
        			}},
        		},
        		{
        			name: "lib_with_svn",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("SVN")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   SVN_URL,
        			}},
        		},
        		{
        			name: "lib_with_hg",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("HG")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   HG_URL,
        			}},
        		},
        		{
        			name: "lib_with_darcs",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("DARCS")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   DARCS_URL,
        			}},
        		},
        		{
        			name: "lib_with_piper",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("PIPER")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_other",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("OTHER")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_local_source",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("LOCAL_SOURCE")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_archive",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("ARCHIVE")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_all_downloads",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN", "GIT")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   GIT_URL,
        			}},
        		},
        		{
        			name: "lib_with_all_downloads_in_different_order",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("DARCS", "GIT", "SVN", "HG")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   GIT_URL,
        			}},
        		},
        		{
        			name: "lib_with_all_but_git",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   SVN_URL,
        			}},
        		},
        		{
        			name: "lib_with_all_but_git_and_svn",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("DARCS", "HG")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   HG_URL,
        			}},
        		},
        		{
        			name: "lib_with_all_nondownloads_and_git",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE", "GIT")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   GIT_URL,
        			}},
        		},
        		{
        			name: "lib_with_all_nondownloads",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE")),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "lib_with_all_nondownloads",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(libWithUrl()),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "versioneddesc",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(NO_NAME_0_1),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "my library",
        				name:          "",
        				version:       "0.1",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "unterminated",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte("name: \"Android\n"),
        			},
        			projects:      []string{"/a"},
        			expectedError: `invalid character '\n' in string`,
        		},
        		{
        			name: "abc",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(EMPTY),
        				"/b/METADATA": []byte(MY_LIB_1_0),
        				"/c/METADATA": []byte(NO_NAME_0_1),
        			},
        			projects: []string{"/a", "/b", "/c"},
        			expected: []pmeta{
        				{
        					project:       "/a",
        					versionedName: "",
        					name:          "",
        					version:       "",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/b",
        					versionedName: "mylib_v_1.0",
        					name:          "mylib",
        					version:       "1.0",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/c",
        					versionedName: "my library",
        					name:          "",
        					version:       "0.1",
        					downloadUrl:   "",
        				},
        			},
        		},
        		{
        			name: "ab",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(EMPTY),
        				"/b/METADATA": []byte(MY_LIB_1_0),
        			},
        			projects: []string{"/a", "/b", "/c"},
        			expected: []pmeta{
        				{
        					project:       "/a",
        					versionedName: "",
        					name:          "",
        					version:       "",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/b",
        					versionedName: "mylib_v_1.0",
        					name:          "mylib",
        					version:       "1.0",
        					downloadUrl:   "",
        				},
        			},
        		},
        		{
        			name: "ac",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(EMPTY),
        				"/c/METADATA": []byte(NO_NAME_0_1),
        			},
        			projects: []string{"/a", "/b", "/c"},
        			expected: []pmeta{
        				{
        					project:       "/a",
        					versionedName: "",
        					name:          "",
        					version:       "",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/c",
        					versionedName: "my library",
        					name:          "",
        					version:       "0.1",
        					downloadUrl:   "",
        				},
        			},
        		},
        		{
        			name: "bc",
        			fs: &testfs.TestFS{
        				"/b/METADATA": []byte(MY_LIB_1_0),
        				"/c/METADATA": []byte(NO_NAME_0_1),
        			},
        			projects: []string{"/a", "/b", "/c"},
        			expected: []pmeta{
        				{
        					project:       "/b",
        					versionedName: "mylib_v_1.0",
        					name:          "mylib",
        					version:       "1.0",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/c",
        					versionedName: "my library",
        					name:          "",
        					version:       "0.1",
        					downloadUrl:   "",
        				},
        			},
        		},
        		{
        			name: "wrongnametype",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(INVALID_NAME),
        			},
        			projects:      []string{"/a"},
        			expectedError: `invalid value for string type`,
        		},
        		{
        			name: "wrongdescriptiontype",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(INVALID_DESCRIPTION),
        			},
        			projects:      []string{"/a"},
        			expectedError: `invalid value for string type`,
        		},
        		{
        			name: "wrongversiontype",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(INVALID_VERSION),
        			},
        			projects:      []string{"/a"},
        			expectedError: `invalid value for string type`,
        		},
        		{
        			name: "wrongtype",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
        			},
        			projects:      []string{"/a"},
        			expectedError: `invalid value for string type`,
        		},
        		{
        			name: "empty",
        			fs: &testfs.TestFS{
        				"/a/METADATA": []byte(EMPTY),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "",
        				name:          "",
        				version:       "",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "emptyother",
        			fs: &testfs.TestFS{
        				"/a/METADATA.bp": []byte(EMPTY),
        			},
        			projects: []string{"/a"},
        		},
        		{
        			name:     "emptyfs",
        			fs:       &testfs.TestFS{},
        			projects: []string{"/a"},
        		},
        		{
        			name: "override",
        			fs: &testfs.TestFS{
        				"/a/METADATA":         []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
        				"/a/METADATA.android": []byte(MY_LIB_1_0),
        			},
        			projects: []string{"/a"},
        			expected: []pmeta{{
        				project:       "/a",
        				versionedName: "mylib_v_1.0",
        				name:          "mylib",
        				version:       "1.0",
        				downloadUrl:   "",
        			}},
        		},
        		{
        			name: "enchilada",
        			fs: &testfs.TestFS{
        				"/a/METADATA":         []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
        				"/a/METADATA.android": []byte(EMPTY),
        				"/b/METADATA":         []byte(MY_LIB_1_0),
        				"/c/METADATA":         []byte(NO_NAME_0_1),
        			},
        			projects: []string{"/a", "/b", "/c"},
        			expected: []pmeta{
        				{
        					project:       "/a",
        					versionedName: "",
        					name:          "",
        					version:       "",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/b",
        					versionedName: "mylib_v_1.0",
        					name:          "mylib",
        					version:       "1.0",
        					downloadUrl:   "",
        				},
        				{
        					project:       "/c",
        					versionedName: "my library",
        					name:          "",
        					version:       "0.1",
        					downloadUrl:   "",
        				},
        			},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			ix := NewIndex(tt.fs)
        			pms, err := ix.MetadataForProjects(tt.projects...)
        			if err != nil {
        				if len(tt.expectedError) == 0 {
        					t.Errorf("unexpected error: got %s, want no error", err)
        				} else if !strings.Contains(err.Error(), tt.expectedError) {
        					t.Errorf("unexpected error: got %s, want %q", err, tt.expectedError)
        				}
        				return
        			}
        			t.Logf("actual %d project metadata", len(pms))
        			for _, pm := range pms {
        				t.Logf("  %v", pm.String())
        			}
        			t.Logf("expected %d project metadata", len(tt.expected))
        			for _, pm := range tt.expected {
        				t.Logf("  %s", pm.String())
        			}
        			if len(tt.expectedError) > 0 {
        				t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
        				return
        			}
        			if len(pms) != len(tt.expected) {
        				t.Errorf("missing project metadata: got %d project metadata, want %d", len(pms), len(tt.expected))
        			}
        			for i := 0; i < len(pms) && i < len(tt.expected); i++ {
        				if msg := tt.expected[i].difference(pms[i]); msg != "" {
        					t.Errorf("unexpected metadata starting at index %d: %s", i, msg)
        					return
        				}
        			}
        			if len(pms) < len(tt.expected) {
        				t.Errorf("missing metadata starting at index %d: got nothing, want %s", len(pms), tt.expected[len(pms)].String())
        			}
        			if len(tt.expected) < len(pms) {
        				t.Errorf("unexpected metadata starting at index %d: got %s, want nothing", len(tt.expected), pms[len(tt.expected)].String())
        			}
        		})
        	}
        }
        
        type pmeta struct {
        	project       string
        	versionedName string
        	name          string
        	version       string
        	downloadUrl   string
        }
        
        func (pm pmeta) String() string {
        	return fmt.Sprintf("project: %q versionedName: %q name: %q version: %q downloadUrl: %q\n", pm.project, pm.versionedName, pm.name, pm.version, pm.downloadUrl)
        }
        
        func (pm pmeta) equals(other *ProjectMetadata) bool {
        	if pm.project != other.project {
        		return false
        	}
        	if pm.versionedName != other.VersionedName() {
        		return false
        	}
        	if pm.name != other.Name() {
        		return false
        	}
        	if pm.version != other.Version() {
        		return false
        	}
        	if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
        		return false
        	}
        	return true
        }
        
        func (pm pmeta) difference(other *ProjectMetadata) string {
        	if pm.equals(other) {
        		return ""
        	}
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "got")
        	if pm.project != other.project {
        		fmt.Fprintf(&sb, " project: %q", other.project)
        	}
        	if pm.versionedName != other.VersionedName() {
        		fmt.Fprintf(&sb, " versionedName: %q", other.VersionedName())
        	}
        	if pm.name != other.Name() {
        		fmt.Fprintf(&sb, " name: %q", other.Name())
        	}
        	if pm.version != other.Version() {
        		fmt.Fprintf(&sb, " version: %q", other.Version())
        	}
        	if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
        		fmt.Fprintf(&sb, " downloadUrl: %q", other.UrlsByTypeName().DownloadUrl())
        	}
        	fmt.Fprintf(&sb, ", want")
        	if pm.project != other.project {
        		fmt.Fprintf(&sb, " project: %q", pm.project)
        	}
        	if pm.versionedName != other.VersionedName() {
        		fmt.Fprintf(&sb, " versionedName: %q", pm.versionedName)
        	}
        	if pm.name != other.Name() {
        		fmt.Fprintf(&sb, " name: %q", pm.name)
        	}
        	if pm.version != other.Version() {
        		fmt.Fprintf(&sb, " version: %q", pm.version)
        	}
        	if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() {
        		fmt.Fprintf(&sb, " downloadUrl: %q", pm.downloadUrl)
        	}
        	return sb.String()
        }
        
        
        ================================================
        FILE: tools/compliance/readgraph.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"io"
        	"io/fs"
        	"os"
        	"strings"
        	"sync"
        
        	"android/soong/compliance/license_metadata_proto"
        
        	"google.golang.org/protobuf/encoding/prototext"
        )
        
        var (
        	// ConcurrentReaders is the size of the task pool for limiting resource usage e.g. open files.
        	ConcurrentReaders = 5
        )
        
        type globalFS struct{}
        
        var _ fs.FS = globalFS{}
        var _ fs.StatFS = globalFS{}
        
        func (s globalFS) Open(name string) (fs.File, error) {
        	return os.Open(name)
        }
        
        func (s globalFS) Stat(name string) (fs.FileInfo, error) {
        	return os.Stat(name)
        }
        
        var FS globalFS
        
        // GetFS returns a filesystem for accessing files under the OUT_DIR environment variable.
        func GetFS(outDir string) fs.FS {
        	if len(outDir) > 0 {
        		return os.DirFS(outDir)
        	}
        	return os.DirFS(".")
        }
        
        // result describes the outcome of reading and parsing a single license metadata file.
        type result struct {
        	// file identifies the path to the license metadata file
        	file string
        
        	// target contains the parsed metadata or nil if an error
        	target *TargetNode
        
        	// err is nil unless an error occurs
        	err error
        }
        
        // receiver coordinates the tasks for reading and parsing license metadata files.
        type receiver struct {
        	// lg accumulates the read metadata and becomes the final resulting LicenseGraph.
        	lg *LicenseGraph
        
        	// rootFS locates the root of the file system from which to read the files.
        	rootFS fs.FS
        
        	// stderr identifies the error output writer.
        	stderr io.Writer
        
        	// task provides a fixed-size task pool to limit concurrent open files etc.
        	task chan bool
        
        	// results returns one license metadata file result at a time.
        	results chan *result
        
        	// wg detects when done
        	wg sync.WaitGroup
        }
        
        // ReadLicenseGraph reads and parses `files` and their dependencies into a LicenseGraph.
        //
        // `files` become the root files of the graph for top-down walks of the graph.
        func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseGraph, error) {
        	if len(files) == 0 {
        		return nil, fmt.Errorf("no license metadata to analyze")
        	}
        	if ConcurrentReaders < 1 {
        		return nil, fmt.Errorf("need at least one task in pool")
        	}
        
        	lg := newLicenseGraph()
        	for _, f := range files {
        		if strings.HasSuffix(f, "meta_lic") {
        			lg.rootFiles = append(lg.rootFiles, f)
        		} else {
        			lg.rootFiles = append(lg.rootFiles, f+".meta_lic")
        		}
        	}
        
        	recv := &receiver{
        		lg:      lg,
        		rootFS:  rootFS,
        		stderr:  stderr,
        		task:    make(chan bool, ConcurrentReaders),
        		results: make(chan *result, ConcurrentReaders),
        		wg:      sync.WaitGroup{},
        	}
        	for i := 0; i < ConcurrentReaders; i++ {
        		recv.task <- true
        	}
        
        	readFiles := func() {
        		lg.mu.Lock()
        		// identify the metadata files to schedule reading tasks for
        		for _, f := range lg.rootFiles {
        			lg.targets[f] = nil
        		}
        		lg.mu.Unlock()
        
        		// schedule tasks to read the files
        		for _, f := range lg.rootFiles {
        			readFile(recv, f)
        		}
        
        		// schedule a task to wait until finished and close the channel.
        		go func() {
        			recv.wg.Wait()
        			close(recv.task)
        			close(recv.results)
        		}()
        	}
        	go readFiles()
        
        	// tasks to read license metadata files are scheduled; read and process results from channel
        	var err error
        	for recv.results != nil {
        		select {
        		case r, ok := <-recv.results:
        			if ok {
        				// handle errors by nil'ing ls, setting err, and clobbering results channel
        				if r.err != nil {
        					err = r.err
        					fmt.Fprintf(recv.stderr, "%s\n", err.Error())
        					lg = nil
        					recv.results = nil
        					continue
        				}
        
        				// record the parsed metadata (guarded by mutex)
        				recv.lg.mu.Lock()
        				lg.targets[r.target.name] = r.target
        				recv.lg.mu.Unlock()
        			} else {
        				// finished -- nil the results channel
        				recv.results = nil
        			}
        		}
        	}
        
        	if lg != nil {
        		esize := 0
        		for _, tn := range lg.targets {
        			esize += len(tn.proto.Deps)
        		}
        		lg.edges = make(TargetEdgeList, 0, esize)
        		for _, tn := range lg.targets {
        			tn.licenseConditions = LicenseConditionSetFromNames(tn.proto.LicenseConditions...)
        			err = addDependencies(lg, tn)
        			if err != nil {
        				return nil, fmt.Errorf("error indexing dependencies for %q: %w", tn.name, err)
        			}
        			tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
        		}
        	}
        	return lg, err
        
        }
        
        // targetNode contains the license metadata for a node in the license graph.
        type targetNode struct {
        	proto license_metadata_proto.LicenseMetadata
        
        	// name is the path to the metadata file.
        	name string
        
        	// lg is the license graph the node belongs to.
        	lg *LicenseGraph
        
        	// edges identifies the dependencies of the target.
        	edges TargetEdgeList
        
        	// licenseConditions identifies the set of license conditions originating at the target node.
        	licenseConditions LicenseConditionSet
        
        	// resolution identifies the set of conditions resolved by acting on the target node.
        	resolution LicenseConditionSet
        
        	// pure indicates whether to treat the node as a pure aggregate (no internal linkage)
        	pure bool
        }
        
        // addDependencies converts the proto AnnotatedDependencies into `edges`
        func addDependencies(lg *LicenseGraph, tn *TargetNode) error {
        	tn.edges = make(TargetEdgeList, 0, len(tn.proto.Deps))
        	for _, ad := range tn.proto.Deps {
        		dependency := ad.GetFile()
        		if len(dependency) == 0 {
        			return fmt.Errorf("missing dependency name")
        		}
        		dtn, ok := lg.targets[dependency]
        		if !ok {
        			return fmt.Errorf("unknown dependency name %q", dependency)
        		}
        		if dtn == nil {
        			return fmt.Errorf("nil dependency for name %q", dependency)
        		}
        		annotations := newEdgeAnnotations()
        		for _, a := range ad.Annotations {
        			// look up a common constant annotation string from a small map
        			// instead of creating 1000's of copies of the same 3 strings.
        			if ann, ok := RecognizedAnnotations[a]; ok {
        				annotations.annotations[ann] = struct{}{}
        			}
        		}
        		edge := &TargetEdge{tn, dtn, annotations}
        		lg.edges = append(lg.edges, edge)
        		tn.edges = append(tn.edges, edge)
        	}
        	return nil
        }
        
        // readFile is a task to read and parse a single license metadata file, and to schedule
        // additional tasks for reading and parsing dependencies as necessary.
        func readFile(recv *receiver, file string) {
        	recv.wg.Add(1)
        	<-recv.task
        	go func() {
        		f, err := recv.rootFS.Open(file)
        		if err != nil {
        			recv.results <- &result{file, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
        			return
        		}
        
        		// read the file
        		data, err := io.ReadAll(f)
        		if err != nil {
        			recv.results <- &result{file, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
        			return
        		}
        		f.Close()
        
        		tn := &TargetNode{lg: recv.lg, name: file}
        
        		err = prototext.Unmarshal(data, &tn.proto)
        		if err != nil {
        			recv.results <- &result{file, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
        			return
        		}
        
        		// send result for this file and release task before scheduling dependencies,
        		// but do not signal done to WaitGroup until dependencies are scheduled.
        		recv.results <- &result{file, tn, nil}
        		recv.task <- true
        
        		// schedule tasks as necessary to read dependencies
        		for _, ad := range tn.proto.Deps {
        			dependency := ad.GetFile()
        			// decide, signal and record whether to schedule task in critical section
        			recv.lg.mu.Lock()
        			_, alreadyScheduled := recv.lg.targets[dependency]
        			if !alreadyScheduled {
        				recv.lg.targets[dependency] = nil
        			}
        			recv.lg.mu.Unlock()
        			// schedule task to read dependency file outside critical section
        			if !alreadyScheduled {
        				readFile(recv, dependency)
        			}
        		}
        
        		// signal task done after scheduling dependencies
        		recv.wg.Done()
        	}()
        }
        
        
        ================================================
        FILE: tools/compliance/readgraph_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"bytes"
        	"sort"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance/testfs"
        )
        
        func TestReadLicenseGraph(t *testing.T) {
        	tests := []struct {
        		name            string
        		fs              *testfs.TestFS
        		roots           []string
        		expectedError   string
        		expectedEdges   []edge
        		expectedTargets []string
        	}{
        		{
        			name: "trivial",
        			fs: &testfs.TestFS{
        				"app.meta_lic": []byte("package_name: \"Android\"\n"),
        			},
        			roots:           []string{"app.meta_lic"},
        			expectedEdges:   []edge{},
        			expectedTargets: []string{"app.meta_lic"},
        		},
        		{
        			name: "unterminated",
        			fs: &testfs.TestFS{
        				"app.meta_lic": []byte("package_name: \"Android\n"),
        			},
        			roots:         []string{"app.meta_lic"},
        			expectedError: `invalid character '\n' in string`,
        		},
        		{
        			name: "danglingref",
        			fs: &testfs.TestFS{
        				"app.meta_lic": []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        			},
        			roots:         []string{"app.meta_lic"},
        			expectedError: `unknown file "lib.meta_lic"`,
        		},
        		{
        			name: "singleedge",
        			fs: &testfs.TestFS{
        				"app.meta_lic": []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        				"lib.meta_lic": []byte(AOSP),
        			},
        			roots:           []string{"app.meta_lic"},
        			expectedEdges:   []edge{{"app.meta_lic", "lib.meta_lic"}},
        			expectedTargets: []string{"app.meta_lic", "lib.meta_lic"},
        		},
        		{
        			name: "fullgraph",
        			fs: &testfs.TestFS{
        				"apex.meta_lic": []byte(AOSP + "deps: {\n  file: \"app.meta_lic\"\n}\ndeps: {\n  file: \"bin.meta_lic\"\n}\n"),
        				"app.meta_lic":  []byte(AOSP),
        				"bin.meta_lic":  []byte(AOSP + "deps: {\n  file: \"lib.meta_lic\"\n}\n"),
        				"lib.meta_lic":  []byte(AOSP),
        			},
        			roots: []string{"apex.meta_lic"},
        			expectedEdges: []edge{
        				{"apex.meta_lic", "app.meta_lic"},
        				{"apex.meta_lic", "bin.meta_lic"},
        				{"bin.meta_lic", "lib.meta_lic"},
        			},
        			expectedTargets: []string{"apex.meta_lic", "app.meta_lic", "bin.meta_lic", "lib.meta_lic"},
        		},
        	}
        	for _, tt := range tests {
        		t.Run(tt.name, func(t *testing.T) {
        			stderr := &bytes.Buffer{}
        			lg, err := ReadLicenseGraph(tt.fs, stderr, tt.roots)
        			if err != nil {
        				if len(tt.expectedError) == 0 {
        					t.Errorf("unexpected error: got %s, want no error", err)
        				} else if !strings.Contains(err.Error(), tt.expectedError) {
        					t.Errorf("unexpected error: got %s, want %q", err, tt.expectedError)
        				}
        				return
        			}
        			if len(tt.expectedError) > 0 {
        				t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
        				return
        			}
        			if lg == nil {
        				t.Errorf("missing license graph: got nil, want license graph")
        				return
        			}
        			actualEdges := make([]edge, 0)
        			for _, e := range lg.Edges() {
        				actualEdges = append(actualEdges, edge{e.Target().Name(), e.Dependency().Name()})
        			}
        			sort.Sort(byEdge(tt.expectedEdges))
        			sort.Sort(byEdge(actualEdges))
        			t.Logf("actualEdges:")
        			for _, edge := range actualEdges {
        				t.Logf("  %s", edge.String())
        			}
        			t.Logf("expectedEdges:")
        			for _, edge := range actualEdges {
        				t.Logf("  %s", edge.String())
        			}
        			if len(tt.expectedEdges) != len(actualEdges) {
        				t.Errorf("len(actualEdges): got %d, want %d", len(actualEdges), len(tt.expectedEdges))
        			} else {
        				for i := 0; i < len(actualEdges); i++ {
        					if tt.expectedEdges[i] != actualEdges[i] {
        						t.Errorf("actualEdges[%d]: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
        					}
        				}
        			}
        
        			actualTargets := make([]string, 0)
        			for _, t := range lg.Targets() {
        				actualTargets = append(actualTargets, t.Name())
        			}
        			sort.Strings(tt.expectedTargets)
        			sort.Strings(actualTargets)
        
        			t.Logf("actualTargets: %v", actualTargets)
        			t.Logf("expectedTargets: %v", tt.expectedTargets)
        
        			if len(tt.expectedTargets) != len(actualTargets) {
        				t.Errorf("len(actualTargets): got %d, want %d", len(actualTargets), len(tt.expectedTargets))
        			} else {
        				for i := 0; i < len(actualTargets); i++ {
        					if tt.expectedTargets[i] != actualTargets[i] {
        						t.Errorf("actualTargets[%d]: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
        					}
        				}
        			}
        		})
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/resolution.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"sort"
        	"strings"
        )
        
        // Resolution describes an action to resolve one or more license conditions.
        //
        // `AttachesTo` identifies the target node that when distributed triggers the action.
        // `ActsOn` identifies the target node that is the object of the action.
        // `Resolves` identifies one or more license conditions that the action resolves.
        //
        // e.g. Suppose an MIT library is linked to a binary that also links to GPL code.
        //
        // A resolution would attach to the binary to share (act on) the MIT library to
        // resolve the restricted condition originating from the GPL code.
        type Resolution struct {
        	attachesTo, actsOn *TargetNode
        	cs                 LicenseConditionSet
        }
        
        // AttachesTo returns the target node the resolution attaches to.
        func (r Resolution) AttachesTo() *TargetNode {
        	return r.attachesTo
        }
        
        // ActsOn returns the target node that must be acted on to resolve the condition.
        //
        // i.e. The node for which notice must be given or whose source must be shared etc.
        func (r Resolution) ActsOn() *TargetNode {
        	return r.actsOn
        }
        
        // Resolves returns the set of license condition the resolution satisfies.
        func (r Resolution) Resolves() LicenseConditionSet {
        	return r.cs
        }
        
        // asString returns a string representation of the resolution.
        func (r Resolution) asString() string {
        	var sb strings.Builder
        	names := r.cs.Names()
        	sort.Strings(names)
        	fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", "))
        	return sb.String()
        }
        
        // ResolutionList represents a partial order of Resolutions ordered by
        // AttachesTo() and ActsOn() leaving `Resolves()` unordered.
        type ResolutionList []Resolution
        
        // Len returns the count of elements in the list.
        func (l ResolutionList) Len() int { return len(l) }
        
        // Swap rearranges 2 elements so that each occupies the other's former position.
        func (l ResolutionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographically less than tht `j`th.
        func (l ResolutionList) Less(i, j int) bool {
        	if l[i].attachesTo.name == l[j].attachesTo.name {
        		return l[i].actsOn.name < l[j].actsOn.name
        	}
        	return l[i].attachesTo.name < l[j].attachesTo.name
        }
        
        // String returns a string representation of the list.
        func (rl ResolutionList) String() string {
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "[")
        	sep := ""
        	for _, r := range rl {
        		fmt.Fprintf(&sb, "%s%s", sep, r.asString())
        		sep = ", "
        	}
        	fmt.Fprintf(&sb, "]")
        	return sb.String()
        }
        
        // AllConditions returns the union of all license conditions resolved by any
        // element of the list.
        func (rl ResolutionList) AllConditions() LicenseConditionSet {
        	result := NewLicenseConditionSet()
        	for _, r := range rl {
        		result = result.Union(r.cs)
        	}
        	return result
        }
        
        // ByName returns the sub-list of resolutions resolving conditions matching
        // `names`.
        func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList {
        	result := make(ResolutionList, 0, rl.CountMatching(conditions))
        	for _, r := range rl {
        		if r.Resolves().MatchesAnySet(conditions) {
        			result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)})
        		}
        	}
        	return result
        }
        
        // CountMatching returns the number of resolutions resolving conditions matching
        // `conditions`.
        func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int {
        	c := 0
        	for _, r := range rl {
        		if r.Resolves().MatchesAnySet(conditions) {
        			c++
        		}
        	}
        	return c
        }
        
        // ByActsOn returns the sub-list of resolutions matching `actsOn`.
        func (rl ResolutionList) ByActsOn(actsOn *TargetNode) ResolutionList {
        	result := make(ResolutionList, 0, rl.CountByActsOn(actsOn))
        	for _, r := range rl {
        		if r.actsOn == actsOn {
        			result = append(result, r)
        		}
        	}
        	return result
        }
        
        // CountByActsOn returns the number of resolutions matching `actsOn`.
        func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {
        	c := 0
        	for _, r := range rl {
        		if r.actsOn == actsOn {
        			c++
        		}
        	}
        	return c
        }
        
        
        ================================================
        FILE: tools/compliance/resolutionset.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"strings"
        )
        
        // ResolutionSet describes an immutable set of targets and the license
        // conditions each target must satisfy or "resolve" in a specific context.
        //
        // Ultimately, the purpose of recording the license metadata and building a
        // license graph is to identify, describe, and verify the necessary actions or
        // operations for compliance policy.
        //
        // i.e. What is the source-sharing policy? Has it been met? Meet it.
        //
        // i.e. Are there incompatible policy requirements? Such as a source-sharing
        // policy applied to code that policy also says may not be shared? If so, stop
        // and remove the dependencies that create the situation.
        //
        // The ResolutionSet is the base unit for mapping license conditions to the
        // targets triggering some necessary action per policy. Different ResolutionSet
        // values may be calculated for different contexts.
        //
        // e.g. Suppose an unencumbered binary links in a notice .a library.
        //
        // An "unencumbered" condition would originate from the binary, and a "notice"
        // condition would originate from the .a library. A ResolutionSet for the
        // context of the Notice policy might attach both conditions to the binary to
        // act on the origin of each condition. By attaching the notice condition to
        // the binary, the ResolutionSet stipulates the policy that the release of the
        // unencumbered binary must provide suitable notice for the .a library.
        //
        // The resulting ResolutionSet could be used for building a notice file, for
        // validating that a suitable notice has been built into the distribution, or
        // for reporting what notices need to be given.
        //
        // The action is defined by the context. In the above example, the action is
        // providing notice for the module acted on. In another context, the action
        // might be sharing the source-code or preserving the privacy of the module
        // acted on.
        type ResolutionSet map[*TargetNode]ActionSet
        
        // AttachesTo identifies the list of targets triggering action to resolve
        // conditions. (unordered)
        func (rs ResolutionSet) AttachesTo() TargetNodeList {
        	result := make(TargetNodeList, 0, len(rs))
        	for attachesTo := range rs {
        		result = append(result, attachesTo)
        	}
        	return result
        }
        
        // AttachesToTarget returns true if the set contains conditions that
        // are `attachedTo`.
        func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {
        	_, isPresent := rs[target]
        	return isPresent
        }
        
        // IsPureAggregate returns true if `target`, which must be in
        // `AttachesTo()` resolves to a pure aggregate in the resolution.
        func (rs ResolutionSet) IsPureAggregate(target *TargetNode) bool {
        	_, isPresent := rs[target]
        	if !isPresent {
        		panic(fmt.Errorf("ResolutionSet.IsPureAggregate(%s): not attached to %s", target.Name(), target.Name()))
        	}
        	return target.pure
        }
        
        // Resolutions returns the list of resolutions that `attachedTo`
        // target must resolve. Returns empty list if no conditions apply.
        func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {
        	as, ok := rs[attachesTo]
        	if !ok {
        		return nil
        	}
        	result := make(ResolutionList, 0, len(as))
        	for actsOn, cs := range as {
        		result = append(result, Resolution{attachesTo, actsOn, cs})
        	}
        	return result
        }
        
        // AllActions returns the set of actions required to resolve the set omitting
        // the attachment.
        func (rs ResolutionSet) AllActions() ActionSet {
        	result := make(ActionSet)
        	for _, as := range rs {
        		for actsOn, cs := range as {
        			if _, ok := result[actsOn]; ok {
        				result[actsOn] = cs.Union(result[actsOn])
        			} else {
        				result[actsOn] = cs
        			}
        		}
        	}
        	return result
        }
        
        // String returns a human-readable string representation of the set.
        func (rs ResolutionSet) String() string {
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "{")
        	sep := ""
        	for attachesTo, as := range rs {
        		fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String())
        		sep = ", "
        	}
        	fmt.Fprintf(&sb, "}")
        	return sb.String()
        }
        
        // ActionSet identifies a set of targets to act on and the license conditions
        // the action will resolve.
        type ActionSet map[*TargetNode]LicenseConditionSet
        
        // String returns a human-readable string representation of the set.
        func (as ActionSet) String() string {
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "{")
        	sep := ""
        	for actsOn, cs := range as {
        		fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String())
        		sep = ", "
        	}
        	fmt.Fprintf(&sb, "}")
        	return sb.String()
        }
        
        
        ================================================
        FILE: tools/compliance/resolutionset_test.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"sort"
        	"testing"
        )
        
        var (
        	// bottomUp describes the bottom-up resolve of a hypothetical graph
        	// the graph has a container image, a couple binaries, and a couple
        	// libraries. bin1 statically links lib1 and dynamically links lib2;
        	// bin2 dynamically links lib1 and statically links lib2.
        	// binc represents a compiler or other toolchain binary used for
        	// building the other binaries.
        	bottomUp = []res{
        		{"image", "image", "notice|restricted"},
        		{"image", "bin1", "reciprocal"},
        		{"image", "bin2", "restricted"},
        		{"image", "lib1", "notice"},
        		{"image", "lib2", "notice"},
        		{"binc", "binc", "proprietary"},
        		{"bin1", "bin1", "reciprocal"},
        		{"bin1", "lib1", "notice"},
        		{"bin2", "bin2", "restricted"},
        		{"bin2", "lib2", "notice"},
        		{"lib1", "lib1", "notice"},
        		{"lib2", "lib2", "notice"},
        	}
        
        	// notice describes bottomUp after a top-down notice resolve.
        	notice = []res{
        		{"image", "image", "notice|restricted"},
        		{"image", "bin1", "reciprocal"},
        		{"image", "bin2", "restricted"},
        		{"image", "lib1", "notice"},
        		{"image", "lib2", "notice|restricted"},
        		{"bin1", "bin1", "reciprocal"},
        		{"bin1", "lib1", "notice"},
        		{"bin2", "bin2", "restricted"},
        		{"bin2", "lib2", "notice|restricted"},
        		{"lib1", "lib1", "notice"},
        		{"lib2", "lib2", "notice"},
        	}
        
        	// share describes bottomUp after a top-down share resolve.
        	share = []res{
        		{"image", "image", "restricted"},
        		{"image", "bin1", "reciprocal"},
        		{"image", "bin2", "restricted"},
        		{"image", "lib2", "restricted"},
        		{"bin1", "bin1", "reciprocal"},
        		{"bin2", "bin2", "restricted"},
        		{"bin2", "lib2", "restricted"},
        	}
        
        	// proprietary describes bottomUp after a top-down proprietary resolve.
        	// Note that the proprietary binc is not reachable through the toolchain
        	// dependency.
        	proprietary = []res{}
        )
        
        func TestResolutionSet_AttachesTo(t *testing.T) {
        	lg := newLicenseGraph()
        
        	rsShare := toResolutionSet(lg, share)
        
        	t.Logf("checking resolution set %s", rsShare.String())
        
        	actual := rsShare.AttachesTo().Names()
        	sort.Strings(actual)
        
        	expected := []string{"bin1", "bin2", "image"}
        
        	t.Logf("actual rsShare: %v", actual)
        	t.Logf("expected rsShare: %v", expected)
        
        	if len(actual) != len(expected) {
        		t.Errorf("rsShare: wrong number of targets: got %d, want %d", len(actual), len(expected))
        		return
        	}
        	for i := 0; i < len(actual); i++ {
        		if actual[i] != expected[i] {
        			t.Errorf("rsShare: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
        		}
        	}
        
        	rsPrivate := toResolutionSet(lg, proprietary)
        	actual = rsPrivate.AttachesTo().Names()
        	expected = []string{}
        
        	t.Logf("actual rsPrivate: %v", actual)
        	t.Logf("expected rsPrivate: %v", expected)
        
        	if len(actual) != len(expected) {
        		t.Errorf("rsPrivate: wrong number of targets: got %d, want %d", len(actual), len(expected))
        		return
        	}
        	for i := 0; i < len(actual); i++ {
        		if actual[i] != expected[i] {
        			t.Errorf("rsPrivate: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
        		}
        	}
        }
        
        func TestResolutionSet_AttachesToTarget(t *testing.T) {
        	lg := newLicenseGraph()
        
        	rsShare := toResolutionSet(lg, share)
        
        	t.Logf("checking resolution set %s", rsShare.String())
        
        	if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
        		t.Errorf("actual.AttachesToTarget(\"binc\"): got true, want false")
        	}
        	if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
        		t.Errorf("actual.AttachesToTarget(\"image\"): got false want true")
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/test_util.go
        ================================================
        // Copyright 2021 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package compliance
        
        import (
        	"fmt"
        	"io"
        	"sort"
        	"strings"
        	"testing"
        
        	"android/soong/tools/compliance/testfs"
        )
        
        const (
        	// AOSP starts a test metadata file for Android Apache-2.0 licensing.
        	AOSP = `` +
        		`package_name: "Android"
        license_kinds: "SPDX-license-identifier-Apache-2.0"
        license_conditions: "notice"
        `
        
        	// GPL starts a test metadata file for GPL 2.0 licensing.
        	GPL = `` +
        		`package_name: "Free Software"
        license_kinds: "SPDX-license-identifier-GPL-2.0"
        license_conditions: "restricted"
        `
        
        	// Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.
        	Classpath = `` +
        		`package_name: "Free Software"
        license_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception"
        license_conditions: "permissive"
        `
        
        	// DependentModule starts a test metadata file for a module in the same package as `Classpath`.
        	DependentModule = `` +
        		`package_name: "Free Software"
        license_kinds: "SPDX-license-identifier-MIT"
        license_conditions: "notice"
        `
        
        	// LGPL starts a test metadata file for a module with LGPL 2.0 licensing.
        	LGPL = `` +
        		`package_name: "Free Library"
        license_kinds: "SPDX-license-identifier-LGPL-2.0"
        license_conditions: "restricted_if_statically_linked"
        `
        
        	// MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
        	MPL = `` +
        		`package_name: "Reciprocal"
        license_kinds: "SPDX-license-identifier-MPL-2.0"
        license_conditions: "reciprocal"
        `
        
        	// MIT starts a test metadata file for a module with generic notice (MIT) licensing.
        	MIT = `` +
        		`package_name: "Android"
        license_kinds: "SPDX-license-identifier-MIT"
        license_conditions: "notice"
        `
        
        	// Proprietary starts a test metadata file for a module with proprietary licensing.
        	Proprietary = `` +
        		`package_name: "Android"
        license_kinds: "legacy_proprietary"
        license_conditions: "proprietary"
        `
        
        	// ByException starts a test metadata file for a module with by_exception_only licensing.
        	ByException = `` +
        		`package_name: "Special"
        license_kinds: "legacy_by_exception_only"
        license_conditions: "by_exception_only"
        `
        )
        
        var (
        	// meta maps test file names to metadata file content without dependencies.
        	meta = map[string]string{
        		"apacheBin.meta_lic":                 AOSP,
        		"apacheLib.meta_lic":                 AOSP,
        		"apacheContainer.meta_lic":           AOSP + "is_container: true\n",
        		"dependentModule.meta_lic":           DependentModule,
        		"gplWithClasspathException.meta_lic": Classpath,
        		"gplBin.meta_lic":                    GPL,
        		"gplLib.meta_lic":                    GPL,
        		"gplContainer.meta_lic":              GPL + "is_container: true\n",
        		"lgplBin.meta_lic":                   LGPL,
        		"lgplLib.meta_lic":                   LGPL,
        		"mitBin.meta_lic":                    MIT,
        		"mitLib.meta_lic":                    MIT,
        		"mplBin.meta_lic":                    MPL,
        		"mplLib.meta_lic":                    MPL,
        		"proprietary.meta_lic":               Proprietary,
        		"by_exception.meta_lic":              ByException,
        	}
        )
        
        // newTestNode constructs a test node in the license graph.
        func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
        	if tn, alreadyExists := lg.targets[targetName]; alreadyExists {
        		return tn
        	}
        	tn := &TargetNode{name: targetName}
        	lg.targets[targetName] = tn
        	return tn
        }
        
        // newTestCondition constructs a test license condition.
        func newTestCondition(conditionName string) LicenseCondition {
        	cl := LicenseConditionSetFromNames(conditionName).AsList()
        	if len(cl) == 0 {
        		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
        	} else if len(cl) != 1 {
        		panic(fmt.Errorf("unexpected multiple conditions from condition name: %q: got %d, want 1", conditionName, len(cl)))
        	}
        	lc := cl[0]
        	return lc
        }
        
        // newTestConditionSet constructs a test license condition set.
        func newTestConditionSet(conditionName []string) LicenseConditionSet {
        	cs := LicenseConditionSetFromNames(conditionName...)
        	if cs.IsEmpty() {
        		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
        	}
        	return cs
        }
        
        // edge describes test data edges to define test graphs.
        type edge struct {
        	target, dep string
        }
        
        // String returns a string representation of the edge.
        func (e edge) String() string {
        	return e.target + " -> " + e.dep
        }
        
        // byEdge orders edges by target then dep name then annotations.
        type byEdge []edge
        
        // Len returns the count of elements in the slice.
        func (l byEdge) Len() int { return len(l) }
        
        // Swap rearranges 2 elements of the slice so that each occupies the other's
        // former position.
        func (l byEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographically less than
        // the `j`th element.
        func (l byEdge) Less(i, j int) bool {
        	if l[i].target == l[j].target {
        		return l[i].dep < l[j].dep
        	}
        	return l[i].target < l[j].target
        }
        
        // annotated describes annotated test data edges to define test graphs.
        type annotated struct {
        	target, dep string
        	annotations []string
        }
        
        func (e annotated) String() string {
        	if e.annotations != nil {
        		return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]"
        	}
        	return e.target + " -> " + e.dep
        }
        
        func (e annotated) IsEqualTo(other annotated) bool {
        	if e.target != other.target {
        		return false
        	}
        	if e.dep != other.dep {
        		return false
        	}
        	if len(e.annotations) != len(other.annotations) {
        		return false
        	}
        	a1 := append([]string{}, e.annotations...)
        	a2 := append([]string{}, other.annotations...)
        	for i := 0; i < len(a1); i++ {
        		if a1[i] != a2[i] {
        			return false
        		}
        	}
        	return true
        }
        
        // toGraph converts a list of roots and a list of annotated edges into a test license graph.
        func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {
        	deps := make(map[string][]annotated)
        	for _, root := range roots {
        		deps[root] = []annotated{}
        	}
        	for _, edge := range edges {
        		if prev, ok := deps[edge.target]; ok {
        			deps[edge.target] = append(prev, edge)
        		} else {
        			deps[edge.target] = []annotated{edge}
        		}
        		if _, ok := deps[edge.dep]; !ok {
        			deps[edge.dep] = []annotated{}
        		}
        	}
        	fs := make(testfs.TestFS)
        	for file, edges := range deps {
        		body := meta[file]
        		for _, edge := range edges {
        			body += fmt.Sprintf("deps: {\n  file: %q\n", edge.dep)
        			for _, ann := range edge.annotations {
        				body += fmt.Sprintf("  annotations: %q\n", ann)
        			}
        			body += "}\n"
        		}
        		fs[file] = []byte(body)
        	}
        
        	return ReadLicenseGraph(&fs, stderr, roots)
        }
        
        // logGraph outputs a representation of the graph to a test log.
        func logGraph(lg *LicenseGraph, t *testing.T) {
        	t.Logf("license graph:")
        	t.Logf("  targets:")
        	for _, target := range lg.Targets() {
        		t.Logf("    %s%s in package %q", target.Name(), target.LicenseConditions().String(), target.PackageName())
        	}
        	t.Logf("  /targets")
        	t.Logf("  edges:")
        	for _, edge := range lg.Edges() {
        		t.Logf("    %s", edge.String())
        	}
        	t.Logf("  /edges")
        	t.Logf("/license graph")
        }
        
        // byAnnotatedEdge orders edges by target then dep name then annotations.
        type byAnnotatedEdge []annotated
        
        func (l byAnnotatedEdge) Len() int      { return len(l) }
        func (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        func (l byAnnotatedEdge) Less(i, j int) bool {
        	if l[i].target == l[j].target {
        		if l[i].dep == l[j].dep {
        			ai := append([]string{}, l[i].annotations...)
        			aj := append([]string{}, l[j].annotations...)
        			sort.Strings(ai)
        			sort.Strings(aj)
        			for k := 0; k < len(ai) && k < len(aj); k++ {
        				if ai[k] == aj[k] {
        					continue
        				}
        				return ai[k] < aj[k]
        			}
        			return len(ai) < len(aj)
        		}
        		return l[i].dep < l[j].dep
        	}
        	return l[i].target < l[j].target
        }
        
        // act describes test data resolution actions to define test action sets.
        type act struct {
        	actsOn, condition string
        }
        
        // String returns a human-readable string representing the test action.
        func (a act) String() string {
        	return fmt.Sprintf("%s{%s}", a.actsOn, a.condition)
        }
        
        // toActionSet converts a list of act test data into a test action set.
        func toActionSet(lg *LicenseGraph, data []act) ActionSet {
        	as := make(ActionSet)
        	for _, a := range data {
        		actsOn := newTestNode(lg, a.actsOn)
        		cs := newTestConditionSet(strings.Split(a.condition, "|"))
        		as[actsOn] = cs
        	}
        	return as
        }
        
        // res describes test data resolutions to define test resolution sets.
        type res struct {
        	attachesTo, actsOn, condition string
        }
        
        // toResolutionSet converts a list of res test data into a test resolution set.
        func toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet {
        	rmap := make(ResolutionSet)
        	for _, r := range data {
        		attachesTo := newTestNode(lg, r.attachesTo)
        		actsOn := newTestNode(lg, r.actsOn)
        		if _, ok := rmap[attachesTo]; !ok {
        			rmap[attachesTo] = make(ActionSet)
        		}
        		cs := newTestConditionSet(strings.Split(r.condition, "|"))
        		rmap[attachesTo][actsOn] |= cs
        	}
        	return rmap
        }
        
        // tcond associates a target name with '|' separated string conditions.
        type tcond struct {
        	target, conditions string
        }
        
        // action represents a single element of an ActionSet for testing.
        type action struct {
        	target *TargetNode
        	cs     LicenseConditionSet
        }
        
        // String returns a human-readable string representation of the action.
        func (a action) String() string {
        	return fmt.Sprintf("%s%s", a.target.Name(), a.cs.String())
        }
        
        // actionList represents an array of actions and a total order defined by
        // target name followed by license condition set.
        type actionList []action
        
        // String returns a human-readable string representation of the list.
        func (l actionList) String() string {
        	var sb strings.Builder
        	fmt.Fprintf(&sb, "[")
        	sep := ""
        	for _, a := range l {
        		fmt.Fprintf(&sb, "%s%s", sep, a.String())
        		sep = ", "
        	}
        	fmt.Fprintf(&sb, "]")
        	return sb.String()
        }
        
        // Len returns the count of elements in the slice.
        func (l actionList) Len() int { return len(l) }
        
        // Swap rearranges 2 elements of the slice so that each occupies the other's
        // former position.
        func (l actionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
        
        // Less returns true when the `i`th element is lexicographically less than
        // the `j`th element.
        func (l actionList) Less(i, j int) bool {
        	if l[i].target == l[j].target {
        		return l[i].cs < l[j].cs
        	}
        	return l[i].target.Name() < l[j].target.Name()
        }
        
        // asActionList represents the resolved license conditions in a license graph
        // as an actionList for comparison in a test.
        func asActionList(lg *LicenseGraph) actionList {
        	result := make(actionList, 0, len(lg.targets))
        	for _, target := range lg.targets {
        		cs := target.resolution
        		if cs.IsEmpty() {
        			continue
        		}
        		result = append(result, action{target, cs})
        	}
        	return result
        }
        
        // toActionList converts an array of tcond into an actionList for comparison
        // in a test.
        func toActionList(lg *LicenseGraph, actions []tcond) actionList {
        	result := make(actionList, 0, len(actions))
        	for _, actn := range actions {
        		target := newTestNode(lg, actn.target)
        		cs := NewLicenseConditionSet()
        		for _, name := range strings.Split(actn.conditions, "|") {
        			lc, ok := RecognizedConditionNames[name]
        			if !ok {
        				panic(fmt.Errorf("Unrecognized test condition name: %q", name))
        			}
        			cs = cs.Plus(lc)
        		}
        		result = append(result, action{target, cs})
        	}
        	return result
        }
        
        // confl defines test data for a SourceSharePrivacyConflict as a target name,
        // source condition name, privacy condition name triple.
        type confl struct {
        	sourceNode, share, privacy string
        }
        
        // toConflictList converts confl test data into an array of
        // SourceSharePrivacyConflict for comparison in a test.
        func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
        	result := make([]SourceSharePrivacyConflict, 0, len(data))
        	for _, c := range data {
        		fields := strings.Split(c.share, ":")
        		cshare := fields[1]
        		fields = strings.Split(c.privacy, ":")
        		cprivacy := fields[1]
        		result = append(result, SourceSharePrivacyConflict{
        			newTestNode(lg, c.sourceNode),
        			newTestCondition(cshare),
        			newTestCondition(cprivacy),
        		})
        	}
        	return result
        }
        
        // checkSameActions compares an actual action set to an expected action set for a test.
        func checkSameActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
        	rsActual := make(ResolutionSet)
        	rsExpected := make(ResolutionSet)
        	testNode := newTestNode(lg, "test")
        	rsActual[testNode] = asActual
        	rsExpected[testNode] = asExpected
        	checkSame(rsActual, rsExpected, t)
        }
        
        // checkSame compares an actual resolution set to an expected resolution set for a test.
        func checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) {
        	t.Logf("actual resolution set: %s", rsActual.String())
        	t.Logf("expected resolution set: %s", rsExpected.String())
        
        	actualTargets := rsActual.AttachesTo()
        	sort.Sort(actualTargets)
        
        	expectedTargets := rsExpected.AttachesTo()
        	sort.Sort(expectedTargets)
        
        	t.Logf("actual targets: %s", actualTargets.String())
        	t.Logf("expected targets: %s", expectedTargets.String())
        
        	for _, target := range expectedTargets {
        		if !rsActual.AttachesToTarget(target) {
        			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
        			continue
        		}
        		expectedRl := rsExpected.Resolutions(target)
        		sort.Sort(expectedRl)
        		actualRl := rsActual.Resolutions(target)
        		sort.Sort(actualRl)
        		if len(expectedRl) != len(actualRl) {
        			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
        				target.name, len(actualRl), len(expectedRl))
        			continue
        		}
        		for i := 0; i < len(expectedRl); i++ {
        			if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
        				t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
        					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
        				continue
        			}
        			expectedConditions := expectedRl[i].Resolves()
        			actualConditions := actualRl[i].Resolves()
        			if expectedConditions != actualConditions {
        				t.Errorf("unexpected conditions apply to %q acting on %q: got %#v with names %s, want %#v with names %s",
        					target.name, expectedRl[i].actsOn.name,
        					actualConditions, actualConditions.Names(),
        					expectedConditions, expectedConditions.Names())
        				continue
        			}
        		}
        
        	}
        	for _, target := range actualTargets {
        		if !rsExpected.AttachesToTarget(target) {
        			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
        		}
        	}
        }
        
        // checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set
        // resolves all of the expected conditions.
        func checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
        	rsActual := make(ResolutionSet)
        	rsExpected := make(ResolutionSet)
        	testNode := newTestNode(lg, "test")
        	rsActual[testNode] = asActual
        	rsExpected[testNode] = asExpected
        	checkResolves(rsActual, rsExpected, t)
        }
        
        // checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set
        // resolves all of the expected conditions.
        func checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) {
        	t.Logf("actual resolution set: %s", rsActual.String())
        	t.Logf("expected resolution set: %s", rsExpected.String())
        
        	actualTargets := rsActual.AttachesTo()
        	sort.Sort(actualTargets)
        
        	expectedTargets := rsExpected.AttachesTo()
        	sort.Sort(expectedTargets)
        
        	t.Logf("actual targets: %s", actualTargets.String())
        	t.Logf("expected targets: %s", expectedTargets.String())
        
        	for _, target := range expectedTargets {
        		if !rsActual.AttachesToTarget(target) {
        			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
        			continue
        		}
        		expectedRl := rsExpected.Resolutions(target)
        		sort.Sort(expectedRl)
        		actualRl := rsActual.Resolutions(target)
        		sort.Sort(actualRl)
        		if len(expectedRl) != len(actualRl) {
        			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
        				target.name, len(actualRl), len(expectedRl))
        			continue
        		}
        		for i := 0; i < len(expectedRl); i++ {
        			if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
        				t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
        					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
        				continue
        			}
        			expectedConditions := expectedRl[i].Resolves()
        			actualConditions := actualRl[i].Resolves()
        			if expectedConditions != (expectedConditions & actualConditions) {
        				t.Errorf("expected conditions missing from %q acting on %q: got %#v with names %s, want %#v with names %s",
        					target.name, expectedRl[i].actsOn.name,
        					actualConditions, actualConditions.Names(),
        					expectedConditions, expectedConditions.Names())
        				continue
        			}
        		}
        
        	}
        	for _, target := range actualTargets {
        		if !rsExpected.AttachesToTarget(target) {
        			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
        		}
        	}
        }
        
        
        ================================================
        FILE: tools/compliance/testfs/Android.bp
        ================================================
        // Copyright (C) 2022 The Android Open Source Project
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package {
            default_applicable_licenses: ["Android-Apache-2.0"],
        }
        
        bootstrap_go_package {
            name: "compliance-test-fs-module",
            srcs: [
                "testfs.go",
            ],
            pkgPath: "android/soong/tools/compliance/testfs",
        }
        
        
        ================================================
        FILE: tools/compliance/testfs/testfs.go
        ================================================
        // Copyright 2022 Google LLC
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package testfs
        
        import (
        	"fmt"
        	"io"
        	"io/fs"
        	"strings"
        	"time"
        )
        
        // TestFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
        type TestFS map[string][]byte
        
        var _ fs.FS = (*TestFS)(nil)
        var _ fs.StatFS = (*TestFS)(nil)
        
        // Open implements fs.FS.Open() to open a file based on the filename.
        func (tfs *TestFS) Open(name string) (fs.File, error) {
        	if _, ok := (*tfs)[name]; !ok {
        		return nil, fmt.Errorf("unknown file %q", name)
        	}
        	return &TestFile{tfs, name, 0}, nil
        }
        
        // Stat implements fs.StatFS.Stat() to examine a file based on the filename.
        func (tfs *TestFS) Stat(name string) (fs.FileInfo, error) {
        	if content, ok := (*tfs)[name]; ok {
        		return &TestFileInfo{name, len(content), 0666}, nil
        	}
        	dirname := name
        	if !strings.HasSuffix(dirname, "/") {
        		dirname = dirname + "/"
        	}
        	for name := range (*tfs) {
        		if strings.HasPrefix(name, dirname) {
        			return &TestFileInfo{name, 8, fs.ModeDir | fs.ModePerm}, nil
        		}
        	}
        	return nil, fmt.Errorf("file not found: %q", name)
        }
        
        // TestFileInfo implements a file info (fs.FileInfo) based on TestFS above.
        type TestFileInfo struct {
        	name string
        	size int
        	mode fs.FileMode
        }
        
        var _ fs.FileInfo = (*TestFileInfo)(nil)
        
        // Name returns the name of the file
        func (fi *TestFileInfo) Name() string {
        	return fi.name
        }
        
        // Size returns the size of the file in bytes.
        func (fi *TestFileInfo) Size() int64 {
        	return int64(fi.size)
        }
        
        // Mode returns the fs.FileMode bits.
        func (fi *TestFileInfo) Mode() fs.FileMode {
        	return fi.mode
        }
        
        // ModTime fakes a modification time.
        func (fi *TestFileInfo) ModTime() time.Time {
        	return time.UnixMicro(0xb0bb)
        }
        
        // IsDir is a synonym for Mode().IsDir()
        func (fi *TestFileInfo) IsDir() bool {
        	return fi.mode.IsDir()
        }
        
        // Sys is unused and returns nil.
        func (fi *TestFileInfo) Sys() any {
        	return nil
        }
        
        // TestFile implements a test file (fs.File) based on TestFS above.
        type TestFile struct {
        	fs   *TestFS
        	name string
        	posn int
        }
        
        var _ fs.File = (*TestFile)(nil)
        
        // Stat not implemented to obviate implementing fs.FileInfo.
        func (f *TestFile) Stat() (fs.FileInfo, error) {
        	return f.fs.Stat(f.name)
        }
        
        // Read copies bytes from the TestFS map.
        func (f *TestFile) Read(b []byte) (int, error) {
        	if f.posn < 0 {
        		return 0, fmt.Errorf("file not open: %q", f.name)
        	}
        	if f.posn >= len((*f.fs)[f.name]) {
        		return 0, io.EOF
        	}
        	n := copy(b, (*f.fs)[f.name][f.posn:])
        	f.posn += n
        	return n, nil
        }
        
        // Close marks the TestFile as no longer in use.
        func (f *TestFile) Close() error {
        	if f.posn < 0 {
        		return fmt.Errorf("file already closed: %q", f.name)
        	}
        	f.posn = -1
        	return nil
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/Android.bp
        ================================================
        package {
            default_applicable_licenses: ["Android-Apache-2.0"],
            default_team: "trendy_team_android_crumpet",
        }
        
        java_binary_host {
            name: "dependency-mapper",
            main_class: "com.android.dependencymapper.Main",
            static_libs: [
                "dependency-mapper-host-lib",
            ],
            visibility: ["//visibility:public"],
        }
        
        java_library_host {
            name: "dependency-mapper-host-lib",
            srcs: [
                "src/**/*.java",
                "proto/**/*.proto",
            ],
            static_libs: [
                "gson",
                "ow2-asm",
            ],
        }
        
        java_test_host {
            name: "dependency-mapper-tests",
            srcs: ["tests/src/**/*.java"],
            static_libs: [
                "junit",
                "dependency-mapper-host-lib",
            ],
            data: [
                "tests/res/**/*",
            ],
            test_options: {
                unit_test: true,
            },
        }
        
        java_library {
            name: "dependency-mapper-test-data",
            srcs: ["tests/res/**/*.java"],
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/OWNERS
        ================================================
        himanshuz@google.com
        
        ================================================
        FILE: tools/dependency_mapper/README.md
        ================================================
        # Dependency Mapper
        
        [dependency-mapper] command line tool. This tool finds the usage based dependencies between java
        files by utilizing byte-code and java file analysis.
        
        # Getting Started
        
        ## Inputs
        * rsp file, containing list of java files separated by whitespace.
        * jar file, containing class files generated after compiling the contents of rsp file.
        
        ## Output
        * proto file, representing the list of dependencies for each java file present in input rsp file,
        represented by [proto/usage.proto]
        
        ## Usage
        ```
        dependency-mapper --src-path [src-list.rsp] --jar-path [classes.jar] --usage-map-path [usage-map.proto]"
        ```
        
        # Notes
        ## Dependencies enlisted are only within the java files present in input.
        ## Ensure that [SourceFile] is present in the classes present in the jar.
        ## To ensure dependencies are listed correctly
        * Classes jar should only contain class files generated from the source rsp files.
        * Classes jar should not exclude any class file that was generated from source rsp files.
        
        ================================================
        FILE: tools/dependency_mapper/proto/dependency.proto
        ================================================
        /*
         * Copyright (C) 2019 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        
        syntax = "proto2";
        
        package com.android.dependencymapper;
        option java_package = "com.android.dependencymapper";
        option java_outer_classname = "DependencyProto";
        
        /**
         * A com.android.dependencymapper.DependencyProto.FileDependency object.
         */
        
        message FileDependency {
        
          // java file path on disk
          optional string file_path = 1;
          // if a change in this file warrants recompiling all files
          optional bool is_dependency_to_all = 2;
          // class files generated when this java file is compiled
          repeated string generated_classes = 3;
          // dependencies of this file.
          repeated string file_dependencies = 4;
        }
        
        /**
         * A com.android.dependencymapper.DependencyProto.FileDependencyList object.
         */
        message FileDependencyList {
        
          // List of java file usages
          repeated FileDependency fileDependency = 1;
        }
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import org.objectweb.asm.signature.SignatureReader;
        import org.objectweb.asm.signature.SignatureVisitor;
        import org.objectweb.asm.ClassReader;
        import org.objectweb.asm.ClassVisitor;
        import org.objectweb.asm.Label;
        import org.objectweb.asm.Opcodes;
        import org.objectweb.asm.Type;
        import org.objectweb.asm.TypePath;
        
        import java.lang.annotation.RetentionPolicy;
        import java.util.HashSet;
        import java.util.Set;
        
        /**
         * An ASM based class visitor to analyze and club all dependencies of a java file.
         * Most of the logic of this class is inspired from
         * gradle incremental compilation
         */
        public class ClassDependenciesVisitor extends ClassVisitor {
        
            private final static int API = Opcodes.ASM9;
        
            private final Set mClassTypes;
            private final Set mConstantsDefined;
            private final Set mInlinedUsages;
            private String mSource;
            private boolean isAnnotationType;
            private boolean mIsDependencyToAll;
            private final RetentionPolicyVisitor retentionPolicyVisitor;
        
            private final ClassRelevancyFilter mClassFilter;
        
            private ClassDependenciesVisitor(ClassReader reader, ClassRelevancyFilter filter) {
                super(API);
                this.mClassTypes = new HashSet<>();
                this.mConstantsDefined = new HashSet<>();
                this.mInlinedUsages =  new HashSet<>();
                this.retentionPolicyVisitor = new RetentionPolicyVisitor();
                this.mClassFilter = filter;
                collectRemainingClassDependencies(reader);
            }
        
            public static ClassDependencyData analyze(
                    String className, ClassReader reader, ClassRelevancyFilter filter) {
                ClassDependenciesVisitor visitor = new ClassDependenciesVisitor(reader, filter);
                reader.accept(visitor, ClassReader.SKIP_FRAMES);
                // Sometimes a class may contain references to the same class, we remove such cases to
                // prevent circular dependency.
                visitor.getClassTypes().remove(className);
                return new ClassDependencyData(Utils.buildPackagePrependedClassSource(
                        className, visitor.getSource()), className, visitor.getClassTypes(),
                        visitor.isDependencyToAll(), visitor.getConstantsDefined(),
                        visitor.getInlinedUsages());
            }
        
            @Override
            public void visitSource(String source, String debug) {
                mSource = source;
            }
        
            @Override
            public void visit(int version, int access, String name, String signature, String superName,
                    String[] interfaces) {
                isAnnotationType = isAnnotationType(interfaces);
                maybeAddClassTypesFromSignature(signature, mClassTypes);
                if (superName != null) {
                    // superName can be null if what we are analyzing is `java.lang.Object`
                    // which can happen when a custom Java SDK is on classpath (typically, android.jar)
                    Type type = Type.getObjectType(superName);
                    maybeAddClassType(mClassTypes, type);
                }
                for (String s : interfaces) {
                    Type interfaceType = Type.getObjectType(s);
                    maybeAddClassType(mClassTypes, interfaceType);
                }
            }
        
            // performs a fast analysis of classes referenced in bytecode (method bodies)
            // avoiding us to implement a costly visitor and potentially missing edge cases
            private void collectRemainingClassDependencies(ClassReader reader) {
                char[] charBuffer = new char[reader.getMaxStringLength()];
                for (int i = 1; i < reader.getItemCount(); i++) {
                    int itemOffset = reader.getItem(i);
                    // see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
                    if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
                        // A CONSTANT_Class entry, read the class descriptor
                        String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
                        Type type = Type.getObjectType(classDescriptor);
                        maybeAddClassType(mClassTypes, type);
                    }
                }
            }
        
            private void maybeAddClassTypesFromSignature(String signature, Set types) {
                if (signature != null) {
                    SignatureReader signatureReader = new SignatureReader(signature);
                    signatureReader.accept(new SignatureVisitor(API) {
                        @Override
                        public void visitClassType(String className) {
                            Type type = Type.getObjectType(className);
                            maybeAddClassType(types, type);
                        }
                    });
                }
            }
        
            protected void maybeAddClassType(Set types, Type type) {
                while (type.getSort() == Type.ARRAY) {
                    type = type.getElementType();
                }
                if (type.getSort() != Type.OBJECT) {
                    return;
                }
                //String name = Utils.classPackageToFilePath(type.getClassName());
                String name = type.getClassName();
                if (mClassFilter.test(name)) {
                    types.add(name);
                }
            }
        
            public String getSource() {
                return mSource;
            }
        
            public Set getClassTypes() {
                return mClassTypes;
            }
        
            public Set getConstantsDefined() {
                return mConstantsDefined;
            }
        
            public Set getInlinedUsages() {
                return mInlinedUsages;
            }
        
            private boolean isAnnotationType(String[] interfaces) {
                return interfaces.length == 1 && interfaces[0].equals("java/lang/annotation/Annotation");
            }
        
            @Override
            public FieldVisitor visitField(
                    int access, String name, String desc, String signature, Object value) {
                maybeAddClassTypesFromSignature(signature, mClassTypes);
                maybeAddClassType(mClassTypes, Type.getType(desc));
                if (isAccessibleConstant(access, value)) {
                    mConstantsDefined.add(value);
                }
                return new FieldVisitor(mClassTypes);
            }
        
            @Override
            public MethodVisitor visitMethod(
                    int access, String name, String desc, String signature, String[] exceptions) {
                maybeAddClassTypesFromSignature(signature, mClassTypes);
                Type methodType = Type.getMethodType(desc);
                maybeAddClassType(mClassTypes, methodType.getReturnType());
                for (Type argType : methodType.getArgumentTypes()) {
                    maybeAddClassType(mClassTypes, argType);
                }
                return new MethodVisitor(mClassTypes);
            }
        
            @Override
            public org.objectweb.asm.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                if (isAnnotationType && "Ljava/lang/annotation/Retention;".equals(desc)) {
                    return retentionPolicyVisitor;
                } else {
                    maybeAddClassType(mClassTypes, Type.getType(desc));
                    return new AnnotationVisitor(mClassTypes);
                }
            }
        
            private static boolean isAccessible(int access) {
                return (access & Opcodes.ACC_PRIVATE) == 0;
            }
        
            private static boolean isAccessibleConstant(int access, Object value) {
                return isConstant(access) && isAccessible(access) && value != null;
            }
        
            private static boolean isConstant(int access) {
                return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;
            }
        
            public boolean isDependencyToAll() {
                return mIsDependencyToAll;
            }
        
            private class FieldVisitor extends org.objectweb.asm.FieldVisitor {
                private final Set types;
        
                public FieldVisitor(Set types) {
                    super(API);
                    this.types = types;
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitAnnotation(
                        String descriptor, boolean visible) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return new AnnotationVisitor(types);
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(int typeRef,
                        TypePath typePath, String descriptor, boolean visible) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return new AnnotationVisitor(types);
                }
            }
        
            private class MethodVisitor extends org.objectweb.asm.MethodVisitor {
                private final Set types;
        
                protected MethodVisitor(Set types) {
                    super(API);
                    this.types = types;
                }
        
                @Override
                public void visitLdcInsn(Object value) {
                    mInlinedUsages.add(value);
                    super.visitLdcInsn(value);
                }
        
                @Override
                public void visitLocalVariable(
                        String name, String desc, String signature, Label start, Label end, int index) {
                    maybeAddClassTypesFromSignature(signature, mClassTypes);
                    maybeAddClassType(mClassTypes, Type.getType(desc));
                    super.visitLocalVariable(name, desc, signature, start, end, index);
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitAnnotation(
                        String descriptor, boolean visible) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return new AnnotationVisitor(types);
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(
                        int parameter, String descriptor, boolean visible) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return new AnnotationVisitor(types);
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(
                        int typeRef, TypePath typePath, String descriptor, boolean visible) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return new AnnotationVisitor(types);
                }
            }
        
            private class RetentionPolicyVisitor extends org.objectweb.asm.AnnotationVisitor {
                public RetentionPolicyVisitor() {
                    super(ClassDependenciesVisitor.API);
                }
        
                @Override
                public void visitEnum(String name, String desc, String value) {
                    if ("Ljava/lang/annotation/RetentionPolicy;".equals(desc)) {
                        RetentionPolicy policy = RetentionPolicy.valueOf(value);
                        if (policy == RetentionPolicy.SOURCE) {
                            mIsDependencyToAll = true;
                        }
                    }
                }
            }
        
            private class AnnotationVisitor extends org.objectweb.asm.AnnotationVisitor {
                private final Set types;
        
                public AnnotationVisitor(Set types) {
                    super(ClassDependenciesVisitor.API);
                    this.types = types;
                }
        
                @Override
                public void visit(String name, Object value) {
                    if (value instanceof Type) {
                        maybeAddClassType(types, (Type) value);
                    }
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitArray(String name) {
                    return this;
                }
        
                @Override
                public org.objectweb.asm.AnnotationVisitor visitAnnotation(String name, String descriptor) {
                    maybeAddClassType(types, Type.getType(descriptor));
                    return this;
                }
            }
        }
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import org.objectweb.asm.ClassReader;
        
        import java.io.IOException;
        import java.io.InputStream;
        import java.nio.file.Path;
        import java.util.ArrayList;
        import java.util.Enumeration;
        import java.util.List;
        import java.util.jar.JarEntry;
        import java.util.jar.JarFile;
        
        /**
         * An utility class that reads each class file present in the classes jar, then analyzes the same,
         * collecting the dependencies in {@link List}
         */
        public class ClassDependencyAnalyzer {
        
            public static List analyze(Path classJar, ClassRelevancyFilter classFilter) {
                List classAnalysisList = new ArrayList<>();
                try (JarFile jarFile = new JarFile(classJar.toFile())) {
                    Enumeration entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        if (entry.getName().endsWith(".class")) {
                            try (InputStream inputStream = jarFile.getInputStream(entry)) {
                                String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
                                ClassDependencyData classAnalysis = ClassDependenciesVisitor.analyze(name,
                                        new ClassReader(inputStream), classFilter);
                                classAnalysisList.add(classAnalysis);
                            }
                        }
                    }
                } catch (IOException e) {
                    System.err.println("Error reading the jar file at: " + classJar);
                    throw new RuntimeException(e);
                }
                return classAnalysisList;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import java.util.Set;
        
        /**
         * Represents the Class Dependency Data collected via ASM analysis.
         */
        public class ClassDependencyData {
            private final String mPackagePrependedClassSource;
            private final String mQualifiedName;
            private final Set mClassDependencies;
            private final boolean mIsDependencyToAll;
            private final Set mConstantsDefined;
            private final Set mInlinedUsages;
        
            public ClassDependencyData(String packagePrependedClassSource, String className,
                    Set classDependencies, boolean isDependencyToAll, Set constantsDefined,
                    Set inlinedUsages) {
                this.mPackagePrependedClassSource = packagePrependedClassSource;
                this.mQualifiedName = className;
                this.mClassDependencies = classDependencies;
                this.mIsDependencyToAll = isDependencyToAll;
                this.mConstantsDefined = constantsDefined;
                this.mInlinedUsages = inlinedUsages;
            }
        
            public String getPackagePrependedClassSource() {
                return mPackagePrependedClassSource;
            }
        
            public String getQualifiedName() {
                return mQualifiedName;
            }
        
            public Set getClassDependencies() {
                return mClassDependencies;
            }
        
            public Set getConstantsDefined() {
                return mConstantsDefined;
            }
        
            public Set inlinedUsages() {
                return mInlinedUsages;
            }
        
            public boolean isDependencyToAll() {
                return mIsDependencyToAll;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import java.util.Set;
        import java.util.function.Predicate;
        
        /**
         * A filter representing the list of class files which are relevant for dependency analysis.
         */
        public class ClassRelevancyFilter implements Predicate {
        
            private final Set mAllowlistedClassNames;
        
            public ClassRelevancyFilter(Set allowlistedClassNames) {
                this.mAllowlistedClassNames = allowlistedClassNames;
            }
        
            @Override
            public boolean test(String className) {
                return mAllowlistedClassNames.contains(className);
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import com.android.dependencymapper.DependencyProto;
        
        import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.HashSet;
        import java.util.List;
        import java.util.Map;
        import java.util.Set;
        
        /**
         * This class binds {@link List} and {@link List} together as a
         * flat map, which represents dependency related attributes of a java file.
         */
        public class DependencyMapper {
            private final List mClassAnalysisList;
            private final List mJavaSourceDataList;
            private final Map mClassToSourceMap = new HashMap<>();
            private final Map> mFileDependencies = new HashMap<>();
            private final Set mDependencyToAll = new HashSet<>();
            private final Map> mSourceToClasses = new HashMap<>();
        
            public DependencyMapper(List classAnalysisList, List javaSourceDataList) {
                this.mClassAnalysisList = classAnalysisList;
                this.mJavaSourceDataList = javaSourceDataList;
            }
        
            public DependencyProto.FileDependencyList buildDependencyMaps() {
                buildClassDependencyMaps();
                buildSourceToClassMap();
                return createFileDependencies();
            }
        
            private void buildClassDependencyMaps() {
                // Create a map between package appended file names and file paths.
                Map sourcePaths = generateSourcePaths();
                // A map between qualified className and its dependencies
                Map> classDependencies = new HashMap<>();
                // A map between constant values and the their declarations.
                Map> constantRegistry = new HashMap<>();
                // A map between constant values and the their inlined usages.
                Map> inlinedUsages = new HashMap<>();
        
                for (ClassDependencyData analysis : mClassAnalysisList) {
                    String className = analysis.getQualifiedName();
        
                    // Compute qualified class name to source path map.
                    String sourceKey = analysis.getPackagePrependedClassSource();
                    String sourcePath = sourcePaths.get(sourceKey);
                    mClassToSourceMap.put(className, sourcePath);
        
                    // compute classDependencies
                    classDependencies.computeIfAbsent(className, k ->
                            new HashSet<>()).addAll(analysis.getClassDependencies());
        
                    // Compute constantRegistry
                    analysis.getConstantsDefined().forEach(c ->
                            constantRegistry.computeIfAbsent(c, k -> new HashSet<>()).add(className));
                    // Compute inlinedUsages map.
                    analysis.inlinedUsages().forEach(u ->
                            inlinedUsages.computeIfAbsent(u, k -> new HashSet<>()).add(className));
        
                    if (analysis.isDependencyToAll()) {
                        mDependencyToAll.add(sourcePath);
                    }
                }
                // Finally build file dependencies
                buildFileDependencies(
                        combineDependencies(classDependencies, inlinedUsages, constantRegistry));
            }
        
            private Map generateSourcePaths() {
                Map sourcePaths = new HashMap<>();
                mJavaSourceDataList.forEach(data ->
                        sourcePaths.put(data.getPackagePrependedFileName(), data.getFilePath()));
                return sourcePaths;
            }
        
            private Map> combineDependencies(Map> classDependencies,
                    Map> inlinedUsages,
                    Map> constantRegistry) {
                Map> combined = new HashMap<>(
                        buildConstantDependencies(inlinedUsages, constantRegistry));
                classDependencies.forEach((k, v) ->
                        combined.computeIfAbsent(k, key -> new HashSet<>()).addAll(v));
                return combined;
            }
        
            private Map> buildConstantDependencies(
                    Map> inlinedUsages, Map> constantRegistry) {
                Map> constantDependencies = new HashMap<>();
                for (Map.Entry> usageEntry : inlinedUsages.entrySet()) {
                    Object usage = usageEntry.getKey();
                    Set usageClasses = usageEntry.getValue();
                    if (constantRegistry.containsKey(usage)) {
                        Set declarationClasses = constantRegistry.get(usage);
                        for (String usageClass : usageClasses) {
                            // Sometimes Usage and Declarations are in the same file, we remove such cases
                            // to prevent circular dependency.
                            declarationClasses.remove(usageClass);
                            constantDependencies.computeIfAbsent(usageClass, k ->
                                    new HashSet<>()).addAll(declarationClasses);
                        }
                    }
                }
        
                return constantDependencies;
            }
        
            private void buildFileDependencies(Map> combinedClassDependencies) {
                combinedClassDependencies.forEach((className, dependencies) -> {
                    String sourceFile = mClassToSourceMap.get(className);
                    if (sourceFile == null) {
                        throw new IllegalArgumentException("Class '" + className
                                + "' does not have a corresponding source file.");
                    }
                    mFileDependencies.computeIfAbsent(sourceFile, k -> new HashSet<>());
                    dependencies.forEach(dependency -> {
                        String dependencySource = mClassToSourceMap.get(dependency);
                        if (dependencySource == null) {
                            throw new IllegalArgumentException("Dependency '" + dependency
                                    + "' does not have a corresponding source file.");
                        }
                        mFileDependencies.get(sourceFile).add(dependencySource);
                    });
                });
            }
        
            private void buildSourceToClassMap() {
                mClassToSourceMap.forEach((className, sourceFile) ->
                        mSourceToClasses.computeIfAbsent(sourceFile, k ->
                                new HashSet<>()).add(className));
            }
        
            private DependencyProto.FileDependencyList createFileDependencies() {
                List fileDependencies = new ArrayList<>();
                mFileDependencies.forEach((file, dependencies) -> {
                    DependencyProto.FileDependency dependency = DependencyProto.FileDependency.newBuilder()
                            .setFilePath(file)
                            .setIsDependencyToAll(mDependencyToAll.contains(file))
                            .addAllGeneratedClasses(mSourceToClasses.get(file))
                            .addAllFileDependencies(dependencies)
                            .build();
                    fileDependencies.add(dependency);
                });
                return DependencyProto.FileDependencyList.newBuilder()
                        .addAllFileDependency(fileDependencies).build();
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import java.io.BufferedReader;
        import java.io.FileReader;
        import java.io.IOException;
        import java.nio.file.Path;
        import java.nio.file.Paths;
        import java.util.ArrayList;
        import java.util.List;
        import java.util.regex.Matcher;
        import java.util.regex.Pattern;
        
        /**
         * An utility class that reads each java file present in the rsp content then analyzes the same,
         * collecting the analysis in {@link List}
         */
        public class JavaSourceAnalyzer {
        
            // Regex that matches against "package abc.xyz.lmn;" declarations in a java file.
            private static final String PACKAGE_REGEX = "^package\\s+([a-zA-Z_][a-zA-Z0-9_.]*);";
        
            public static List analyze(Path srcRspFile) {
                List javaSourceDataList = new ArrayList<>();
                try (BufferedReader reader = new BufferedReader(new FileReader(srcRspFile.toFile()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        // Split the line by spaces, tabs, multiple java files can be on a single line.
                        String[] files = line.trim().split("\\s+");
                        for (String file : files) {
                            Path p = Paths.get("", file);
                            System.out.println(p.toAbsolutePath().toString());
                            javaSourceDataList
                                    .add(new JavaSourceData(file, constructPackagePrependedFileName(file)));
                        }
                    }
                } catch (IOException e) {
                    System.err.println("Error reading rsp file at: " + srcRspFile);
                    throw new RuntimeException(e);
                }
                return javaSourceDataList;
            }
        
            private static String constructPackagePrependedFileName(String filePath) {
                String packageAppendedFileName = null;
                // if the file path is abc/def/ghi/JavaFile.java we extract JavaFile.java
                String javaFileName = filePath.substring(filePath.lastIndexOf("/") + 1);
                try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
                    String line;
                    // Process each line and match against the package regex pattern.
                    while ((line = reader.readLine()) != null) {
                        Pattern pattern = Pattern.compile(PACKAGE_REGEX);
                        Matcher matcher = pattern.matcher(line);
                        if (matcher.find()) {
                            packageAppendedFileName = matcher.group(1) + "." + javaFileName;
                            break;
                        }
                    }
                } catch (IOException e) {
                    System.err.println("Error reading java file at: " + filePath);
                    throw new RuntimeException(e);
                }
                // Should not be null
                assert packageAppendedFileName != null;
                return packageAppendedFileName;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        /**
         * POJO representing the data collected from Java Source file analysis.
         */
        public class JavaSourceData {
        
            private final String mFilePath;
            private final String mPackagePrependedFileName;
        
            public JavaSourceData(String filePath, String packagePrependedFileName) {
                mFilePath = filePath;
                mPackagePrependedFileName = packagePrependedFileName;
            }
        
            public String getFilePath() {
                return mFilePath;
            }
        
            public String getPackagePrependedFileName() {
                return mPackagePrependedFileName;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/Main.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import static com.android.dependencymapper.Utils.listClassesInJar;
        
        import com.android.dependencymapper.DependencyProto;
        
        import java.io.IOException;
        import java.nio.file.Files;
        import java.nio.file.Path;
        import java.util.List;
        import java.util.Set;
        
        public class Main {
        
            public static void main(String[] args) throws IOException, InterruptedException {
                try {
                    InputData input = parseAndValidateInput(args);
                    generateDependencyMap(input);
                } catch (IllegalArgumentException e) {
                    System.err.println("Error: " + e.getMessage());
                    showUsage();
                }
            }
        
            private static class InputData {
                public Path srcList;
                public Path classesJar;
                public Path dependencyMapProto;
        
                public InputData(Path srcList, Path classesJar, Path dependencyMapProto) {
                    this.srcList = srcList;
                    this.classesJar = classesJar;
                    this.dependencyMapProto = dependencyMapProto;
                }
            }
        
            private static InputData parseAndValidateInput(String[] args) {
                for (String arg : args) {
                    if ("--help".equals(arg)) {
                        showUsage();
                        System.exit(0); // Indicate successful exit after showing help
                    }
                }
        
                if (args.length != 6) { // Explicitly check for the correct number of arguments
                    throw new IllegalArgumentException("Incorrect number of arguments");
                }
        
                Path srcList = null;
                Path classesJar = null;
                Path dependencyMapProto = null;
        
                for (int i = 0; i < args.length; i += 2) {
                    String arg = args[i].trim();
                    String argValue = args[i + 1].trim();
        
                    switch (arg) {
                        case "--src-path" -> srcList = Path.of(argValue);
                        case "--jar-path" -> classesJar = Path.of(argValue);
                        case "--dependency-map-path" -> dependencyMapProto = Path.of(argValue);
                        default -> throw new IllegalArgumentException("Unknown argument: " + arg);
                    }
                }
        
                // Validate file existence and readability
                validateFile(srcList, "--src-path");
                validateFile(classesJar, "--jar-path");
        
                return new InputData(srcList, classesJar, dependencyMapProto);
            }
        
            private static void validateFile(Path path, String argName) {
                if (path == null) {
                    throw new IllegalArgumentException(argName + " is required");
                }
                if (!Files.exists(path)) {
                    throw new IllegalArgumentException(argName + " does not exist: " + path);
                }
                if (!Files.isReadable(path)) {
                    throw new IllegalArgumentException(argName + " is not readable: " + path);
                }
            }
        
            private static void generateDependencyMap(InputData input) {
                // First collect all classes in the jar.
                Set classesInJar = listClassesInJar(input.classesJar);
                // Perform dependency analysis.
                List classDependencyDataList = ClassDependencyAnalyzer
                        .analyze(input.classesJar, new ClassRelevancyFilter(classesInJar));
                // Perform java source analysis.
                List javaSourceDataList = JavaSourceAnalyzer.analyze(input.srcList);
                // Collect all dependencies and map them as DependencyProto.FileDependencyList
                DependencyMapper dp = new DependencyMapper(classDependencyDataList, javaSourceDataList);
                DependencyProto.FileDependencyList dependencyList =  dp.buildDependencyMaps();
        
                // Write the proto to output file
                Utils.writeContentsToProto(dependencyList, input.dependencyMapProto);
            }
        
            private static void showUsage() {
                System.err.println(
                        "Usage: dependency-mapper "
                                + "--src-path [src-list.rsp] "
                                + "--jar-path [classes.jar] "
                                + "--dependency-map-path [dependency-map.proto]");
            }
        
        }
        
        ================================================
        FILE: tools/dependency_mapper/src/com/android/dependencymapper/Utils.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import com.android.dependencymapper.DependencyProto;
        
        import com.google.gson.Gson;
        import com.google.gson.GsonBuilder;
        
        import java.io.FileWriter;
        import java.io.IOException;
        import java.io.OutputStream;
        import java.nio.file.Files;
        import java.nio.file.Path;
        import java.util.Enumeration;
        import java.util.HashMap;
        import java.util.HashSet;
        import java.util.Map;
        import java.util.Set;
        import java.util.jar.JarEntry;
        import java.util.jar.JarFile;
        
        public class Utils {
        
            public static String trimAndConvertToPackageBasedPath(String fileBasedPath) {
                // Remove ".class" from the fileBasedPath, then replace "/" with "."
                return fileBasedPath.replaceAll("\\..*", "").replaceAll("/", ".");
            }
        
            public static String buildPackagePrependedClassSource(String qualifiedClassPath,
                    String classSource) {
                // Find the location of the start of classname in the qualifiedClassPath
                int classNameSt = qualifiedClassPath.lastIndexOf(".") + 1;
                // Replace the classname in qualifiedClassPath with classSource
                return qualifiedClassPath.substring(0, classNameSt) + classSource;
            }
        
            public static void writeContentsToJson(DependencyProto.FileDependencyList contents, Path jsonOut) {
                Gson gson = new GsonBuilder().setPrettyPrinting().create();
                Map> jsonMap = new HashMap<>();
                for (DependencyProto.FileDependency fileDependency : contents.getFileDependencyList()) {
                    jsonMap.putIfAbsent(fileDependency.getFilePath(),
                            Set.copyOf(fileDependency.getFileDependenciesList()));
                }
                String json = gson.toJson(jsonMap);
                try (FileWriter file = new FileWriter(jsonOut.toFile())) {
                    file.write(json);
                } catch (IOException e) {
                    System.err.println("Error writing json output to: " + jsonOut);
                    throw new RuntimeException(e);
                }
            }
        
            public static void writeContentsToProto(DependencyProto.FileDependencyList usages, Path protoOut) {
                try {
                    OutputStream outputStream = Files.newOutputStream(protoOut);
                    usages.writeDelimitedTo(outputStream);
                } catch (IOException e) {
                    System.err.println("Error writing proto output to: " + protoOut);
                    throw new RuntimeException(e);
                }
            }
        
            public static Set listClassesInJar(Path classesJarPath) {
                Set classes = new HashSet<>();
                try (JarFile jarFile = new JarFile(classesJarPath.toFile())) {
                    Enumeration entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        if (entry.getName().endsWith(".class")) {
                            String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
                            classes.add(name);
                        }
                    }
                } catch (IOException e) {
                    System.err.println("Error reading the jar file at: " + classesJarPath);
                    throw new RuntimeException(e);
                }
                return classes;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.annotation;
        
        @res.testdata.annotation.RuntimeAnnotation
        public class AnnotationUsage {
        
            private final int mSourceAnnField;
        
            public AnnotationUsage(@res.testdata.annotation.SourceAnnotation int sourceAnnField) {
                mSourceAnnField = sourceAnnField;
            }
        
            public @res.testdata.annotation.SourceAnnotation int getSourceAnnField() {
                return mSourceAnnField;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.annotation;
        
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        
        @Retention(RetentionPolicy.RUNTIME)
        public @interface RuntimeAnnotation {
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.annotation;
        
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        
        @Retention(RetentionPolicy.SOURCE)
        public @interface SourceAnnotation {
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.constants;
        
        public class ConstantDefinition {
            public static final String TEST_CONSTANT = "test_constant";
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.constants;
        
        public class ConstantUsage {
        
            public ConstantUsage(){}
        
            public String useConstantInMethodBody() {
                return res.testdata.constants.ConstantDefinition.TEST_CONSTANT;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.inheritance;
        
        public class BaseClass {
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.inheritance;
        
        public interface BaseImpl {
        
            void baseImpl();
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.inheritance;
        
        public class InheritanceUsage extends res.testdata.inheritance.BaseClass implements
                res.testdata.inheritance.BaseImpl {
            @Override
            public void baseImpl() {
        
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.methods;
        
        public class FieldUsage {
        
            private res.testdata.methods.ReferenceClass1 mReferenceClass1;
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.methods;
        
        public class MethodUsage {
        
            public void methodReferences(res.testdata.methods.ReferenceClass1 mReferenceClass1) {
                res.testdata.methods.ReferenceClass2 referenceClass2 =
                        new res.testdata.methods.ReferenceClass2();
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.methods;
        
        public class ReferenceClass1 {
        
            public ReferenceClass1(){}
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package res.testdata.methods;
        
        public class ReferenceClass2 {
            public ReferenceClass2(){}
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/res/testfiles/sources.rsp
        ================================================
        tests/res/testdata/annotation/AnnotationUsage.java
        tests/res/testdata/annotation/SourceAnnotation.java
        tests/res/testdata/annotation/RuntimeAnnotation.java
        tests/res/testdata/constants/ConstantDefinition.java
        tests/res/testdata/constants/ConstantUsage.java
        tests/res/testdata/inheritance/InheritanceUsage.java
        tests/res/testdata/inheritance/BaseClass.java
        tests/res/testdata/inheritance/BaseImpl.java
        tests/res/testdata/methods/FieldUsage.java
        tests/res/testdata/methods/MethodUsage.java
        tests/res/testdata/methods/ReferenceClass1.java
        tests/res/testdata/methods/ReferenceClass2.java
        
        ================================================
        FILE: tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import static com.android.dependencymapper.Utils.listClassesInJar;
        
        import static org.junit.Assert.assertFalse;
        import static org.junit.Assert.assertTrue;
        
        import org.junit.BeforeClass;
        import org.junit.Test;
        
        import java.net.URISyntaxException;
        import java.nio.file.Path;
        import java.nio.file.Paths;
        import java.util.HashSet;
        import java.util.List;
        import java.util.Set;
        
        public class ClassDependencyAnalyzerTest {
        
            private static List mClassDependencyDataList;
        
            private static final String CLASSES_JAR_PATH =
                    "tests/res/testfiles/dependency-mapper-test-data.jar";
        
            @BeforeClass
            public static void beforeClass() throws URISyntaxException {
                Path path = Paths.get(CLASSES_JAR_PATH);
                Set classesInJar = listClassesInJar(path);
                // Perform dependency analysis.
                mClassDependencyDataList = ClassDependencyAnalyzer.analyze(path,
                        new ClassRelevancyFilter(classesInJar));
            }
        
            @Test
            public void testAnnotationDeps(){
                String annoClass = "res.testdata.annotation.AnnotationUsage";
                String sourceAnno = "res.testdata.annotation.SourceAnnotation";
                String runTimeAnno = "res.testdata.annotation.RuntimeAnnotation";
        
                dependencyVerifier(annoClass,
                        new HashSet<>(List.of(runTimeAnno)), new HashSet<>(List.of(sourceAnno)));
        
                for (ClassDependencyData dep : mClassDependencyDataList) {
                    if (dep.getQualifiedName().equals(sourceAnno)) {
                        assertTrue(sourceAnno + " is not dependencyToAll ", dep.isDependencyToAll());
                    }
                    if (dep.getQualifiedName().equals(runTimeAnno)) {
                        assertFalse(runTimeAnno + " is dependencyToAll ", dep.isDependencyToAll());
                    }
                }
            }
        
            @Test
            public void testConstantsDeps(){
                String constDefined = "test_constant";
                String constDefClass = "res.testdata.constants.ConstantDefinition";
                String constUsageClass = "res.testdata.constants.ConstantUsage";
        
                boolean constUsageClassFound = false;
                boolean constDefClassFound = false;
                for (ClassDependencyData dep : mClassDependencyDataList) {
                    if (dep.getQualifiedName().equals(constUsageClass)) {
                        constUsageClassFound = true;
                        assertTrue("InlinedUsage of : " + constDefined + " not found",
                                dep.inlinedUsages().contains(constDefined));
                    }
                    if (dep.getQualifiedName().equals(constDefClass)) {
                        constDefClassFound = true;
                        assertTrue("Constant " + constDefined + " not defined",
                                dep.getConstantsDefined().contains(constDefined));
                    }
                }
                assertTrue("Class " + constUsageClass + " not found", constUsageClassFound);
                assertTrue("Class " + constDefClass + " not found", constDefClassFound);
            }
        
            @Test
            public void testInheritanceDeps(){
                String sourceClass = "res.testdata.inheritance.InheritanceUsage";
                String baseClass = "res.testdata.inheritance.BaseClass";
                String baseImpl = "res.testdata.inheritance.BaseImpl";
        
                dependencyVerifier(sourceClass,
                        new HashSet<>(List.of(baseClass, baseImpl)), new HashSet<>());
            }
        
        
            @Test
            public void testMethodDeps(){
                String fieldUsage = "res.testdata.methods.FieldUsage";
                String methodUsage = "res.testdata.methods.MethodUsage";
                String ref1 = "res.testdata.methods.ReferenceClass1";
                String ref2 = "res.testdata.methods.ReferenceClass2";
        
                dependencyVerifier(fieldUsage,
                        new HashSet<>(List.of(ref1)), new HashSet<>(List.of(ref2)));
                dependencyVerifier(methodUsage,
                        new HashSet<>(List.of(ref1, ref2)), new HashSet<>());
            }
        
            private void dependencyVerifier(String qualifiedName, Set deps, Set nonDeps) {
                boolean depFound = false;
                for (ClassDependencyData classDependencyData : mClassDependencyDataList) {
                    if (classDependencyData.getQualifiedName().equals(qualifiedName)) {
                        depFound = true;
                        for (String dep : deps) {
                            assertTrue(qualifiedName + " does not depends on " + dep,
                                    classDependencyData.getClassDependencies().contains(dep));
                        }
                        for (String nonDep : nonDeps) {
                            assertFalse(qualifiedName + " depends on " + nonDep,
                                    classDependencyData.getClassDependencies().contains(nonDep));
                        }
                    }
                }
                assertTrue("Class " + qualifiedName + " not found", depFound);
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import static com.android.dependencymapper.Utils.listClassesInJar;
        
        import static org.junit.Assert.assertFalse;
        import static org.junit.Assert.assertNotEquals;
        
        import com.android.dependencymapper.ClassDependencyAnalyzer;
        import com.android.dependencymapper.ClassDependencyData;
        import com.android.dependencymapper.ClassRelevancyFilter;
        
        import org.junit.Test;
        
        import java.nio.file.Path;
        import java.nio.file.Paths;
        import java.util.List;
        import java.util.Set;
        
        public class ClassRelevancyFilterTest {
        
            private static final String CLASSES_JAR_PATH =
                    "tests/res/testfiles/dependency-mapper-test-data.jar";
        
            @Test
            public void testClassRelevancyFilter() {
                Path path = Paths.get(CLASSES_JAR_PATH);
                Set classesInJar = listClassesInJar(path);
        
                // Add a relevancy filter that skips a class.
                String skippedClass = "res.testdata.BaseClass";
                classesInJar.remove(skippedClass);
        
                // Perform dependency analysis.
                List classDependencyDataList =
                        ClassDependencyAnalyzer.analyze(path, new ClassRelevancyFilter(classesInJar));
        
                // check that the skipped class is not present in classDepsList
                for (ClassDependencyData dep : classDependencyDataList) {
                    assertNotEquals("SkippedClass " + skippedClass + " is present",
                            skippedClass, dep.getQualifiedName());
                    assertFalse("SkippedClass " + skippedClass + " is present as dependency of " + dep,
                            dep.getClassDependencies().contains(skippedClass));
                }
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import static org.junit.Assert.assertEquals;
        import static org.junit.Assert.assertFalse;
        import static org.junit.Assert.assertNotNull;
        import static org.junit.Assert.assertTrue;
        
        import org.junit.BeforeClass;
        import org.junit.Test;
        
        import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.HashSet;
        import java.util.List;
        import java.util.Map;
        
        public class DependencyMapperTest {
        
            private static final List mJavaSourceData = new ArrayList<>();
            private static final List mClassDependencyData = new ArrayList<>();
        
            private static Map  mFileDependencyMap;
        
            public static String AUDIO_CONS = "AUDIO_CONS";
            public static String AUDIO_CONS_PATH = "frameworks/base/audio/AudioPermission.java";
            public static String AUDIO_CONS_PACKAGE = "com.android.audio.AudioPermission";
        
            public static String AUDIO_TONE_CONS_1 = "AUDIO_TONE_CONS_1";
            public static String AUDIO_TONE_CONS_2 = "AUDIO_TONE_CONS_2";
            public static String AUDIO_TONE_CONS_PATH = "frameworks/base/audio/Audio$Tones.java";
            public static String AUDIO_TONE_CONS_PACKAGE = "com.android.audio.Audio$Tones";
        
            public static String ST_MANAGER_PATH = "frameworks/base/core/storage/StorageManager.java";
            public static String ST_MANAGER_PACKAGE = "com.android.storage.StorageManager";
        
            public static String CONST_OUTSIDE_SCOPE = "CONST_OUTSIDE_SCOPE";
            public static String PERM_MANAGER_PATH =  "frameworks/base/core/permission/PermissionManager.java";
            public static String PERM_MANAGER_PACKAGE =  "com.android.permission.PermissionManager";
        
            public static String SOURCE_ANNO_PATH = "frameworks/base/anno/SourceAnno.java";
            public static String SOURCE_ANNO_PACKAGE = "com.android.anno.SourceAnno";
        
            public static String PERM_SOURCE_PATH = "frameworks/base/core/permission/PermissionSources.java";
            public static String PERM_SOURCE_PACKAGE = "com.android.permission.PermissionSources";
        
            public static String PERM_DATA_PATH = "frameworks/base/core/permission/PermissionSources$Data.java";
            public static String PERM_DATA_PACKAGE = "com.android.permission.PermissionSources$Data";
        
            static {
                JavaSourceData audioConstants = new JavaSourceData(AUDIO_CONS_PATH, AUDIO_CONS_PACKAGE + ".java");
                JavaSourceData audioToneConstants =
                        new JavaSourceData(AUDIO_TONE_CONS_PATH, AUDIO_TONE_CONS_PACKAGE + ".java"); //f2
                JavaSourceData stManager = new JavaSourceData( ST_MANAGER_PATH, ST_MANAGER_PACKAGE + ".java");
                JavaSourceData permManager = new JavaSourceData(PERM_MANAGER_PATH, PERM_MANAGER_PACKAGE + ".java");
                JavaSourceData permSource = new JavaSourceData(PERM_SOURCE_PATH, PERM_SOURCE_PACKAGE + ".java");
                JavaSourceData permSourceData = new JavaSourceData(PERM_DATA_PATH, PERM_DATA_PACKAGE + ".java");
        
                JavaSourceData sourceNotPresentInClass =
                        new JavaSourceData(SOURCE_ANNO_PATH, SOURCE_ANNO_PACKAGE);
        
                mJavaSourceData.addAll(List.of(audioConstants, audioToneConstants, stManager,
                        permManager, permSource, permSourceData, sourceNotPresentInClass));
        
                ClassDependencyData audioConstantsDeps =
                        new ClassDependencyData(AUDIO_CONS_PACKAGE + ".java",
                                AUDIO_CONS_PACKAGE, new HashSet<>(), false,
                                new HashSet<>(List.of(AUDIO_CONS)), new HashSet<>());
        
                ClassDependencyData audioToneConstantsDeps =
                        new ClassDependencyData(AUDIO_TONE_CONS_PACKAGE + ".java",
                                AUDIO_TONE_CONS_PACKAGE, new HashSet<>(), false,
                                new HashSet<>(List.of(AUDIO_TONE_CONS_1, AUDIO_TONE_CONS_2)),
                                new HashSet<>());
        
                ClassDependencyData stManagerDeps =
                        new ClassDependencyData(ST_MANAGER_PACKAGE + ".java",
                                ST_MANAGER_PACKAGE, new HashSet<>(List.of(PERM_SOURCE_PACKAGE)), false,
                                new HashSet<>(), new HashSet<>(List.of(AUDIO_CONS, AUDIO_TONE_CONS_1)));
        
                ClassDependencyData permManagerDeps =
                        new ClassDependencyData(PERM_MANAGER_PACKAGE + ".java", PERM_MANAGER_PACKAGE,
                                new HashSet<>(List.of(PERM_SOURCE_PACKAGE, PERM_DATA_PACKAGE)), false,
                                new HashSet<>(), new HashSet<>(List.of(CONST_OUTSIDE_SCOPE)));
        
                ClassDependencyData permSourceDeps =
                        new ClassDependencyData(PERM_SOURCE_PACKAGE + ".java",
                                PERM_SOURCE_PACKAGE, new HashSet<>(), false,
                                new HashSet<>(), new HashSet<>());
        
                ClassDependencyData permSourceDataDeps =
                        new ClassDependencyData(PERM_DATA_PACKAGE + ".java",
                                PERM_DATA_PACKAGE, new HashSet<>(), false,
                                new HashSet<>(), new HashSet<>());
        
                mClassDependencyData.addAll(List.of(audioConstantsDeps, audioToneConstantsDeps,
                        stManagerDeps, permManagerDeps, permSourceDeps, permSourceDataDeps));
            }
        
            @BeforeClass
            public static void beforeAll(){
                mFileDependencyMap = buildActualDepsMap(
                        new DependencyMapper(mClassDependencyData, mJavaSourceData).buildDependencyMaps());
            }
        
            @Test
            public void testFileDependencies() {
                // Test for AUDIO_CONS_PATH
                DependencyProto.FileDependency audioDepsActual = mFileDependencyMap.get(AUDIO_CONS_PATH);
                assertNotNull(AUDIO_CONS_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 0 dependencies.
                validateDependencies(audioDepsActual, AUDIO_CONS_PATH, 0, new ArrayList<>());
        
                // Test for AUDIO_TONE_CONS_PATH
                DependencyProto.FileDependency audioToneDepsActual =
                        mFileDependencyMap.get(AUDIO_TONE_CONS_PATH);
                assertNotNull(AUDIO_TONE_CONS_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 0 dependencies.
                validateDependencies(audioToneDepsActual, AUDIO_TONE_CONS_PATH, 0, new ArrayList<>());
        
                // Test for ST_MANAGER_PATH
                DependencyProto.FileDependency stManagerDepsActual =
                        mFileDependencyMap.get(ST_MANAGER_PATH);
                assertNotNull(ST_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 3 dependencies.
                validateDependencies(stManagerDepsActual, ST_MANAGER_PATH, 3,
                        new ArrayList<>(List.of(AUDIO_CONS_PATH, AUDIO_TONE_CONS_PATH, PERM_SOURCE_PATH)));
        
                // Test for PERM_MANAGER_PATH
                DependencyProto.FileDependency permManagerDepsActual =
                        mFileDependencyMap.get(PERM_MANAGER_PATH);
                assertNotNull(PERM_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 2 dependencies.
                validateDependencies(permManagerDepsActual, PERM_MANAGER_PATH, 2,
                        new ArrayList<>(List.of(PERM_SOURCE_PATH, PERM_DATA_PATH)));
        
                // Test for PERM_SOURCE_PATH
                DependencyProto.FileDependency permSourceDepsActual =
                        mFileDependencyMap.get(PERM_SOURCE_PATH);
                assertNotNull(PERM_SOURCE_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 0 dependencies.
                validateDependencies(permSourceDepsActual, PERM_SOURCE_PATH, 0, new ArrayList<>());
        
                // Test for PERM_DATA_PATH
                DependencyProto.FileDependency permDataDepsActual =
                        mFileDependencyMap.get(PERM_DATA_PATH);
                assertNotNull(PERM_DATA_PATH + " not found in dependencyList", audioDepsActual);
                // This file should have 0 dependencies.
                validateDependencies(permDataDepsActual, PERM_DATA_PATH, 0, new ArrayList<>());
            }
        
            private void validateDependencies(DependencyProto.FileDependency dependency, String fileName, int fileDepsCount, List fileDeps) {
                assertEquals(fileName + " does not have expected dependencies", fileDepsCount, dependency.getFileDependenciesCount());
                assertTrue(fileName + " does not have expected dependencies", dependency.getFileDependenciesList().containsAll(fileDeps));
            }
        
            private static Map buildActualDepsMap(
                    DependencyProto.FileDependencyList fileDependencyList) {
                Map dependencyMap = new HashMap<>();
                for (DependencyProto.FileDependency fileDependency : fileDependencyList.getFileDependencyList()) {
                    if (fileDependency.getFilePath().equals(AUDIO_CONS_PATH)) {
                        dependencyMap.put(AUDIO_CONS_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(AUDIO_TONE_CONS_PATH)) {
                        dependencyMap.put(AUDIO_TONE_CONS_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(ST_MANAGER_PATH)) {
                        dependencyMap.put(ST_MANAGER_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(PERM_MANAGER_PATH)) {
                        dependencyMap.put(PERM_MANAGER_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(PERM_SOURCE_PATH)) {
                        dependencyMap.put(PERM_SOURCE_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(PERM_DATA_PATH)) {
                        dependencyMap.put(PERM_DATA_PATH, fileDependency);
                    }
                    if (fileDependency.getFilePath().equals(SOURCE_ANNO_PATH)) {
                        dependencyMap.put(SOURCE_ANNO_PATH, fileDependency);
                    }
                }
                assertFalse(SOURCE_ANNO_PATH + " found in dependencyList",
                        dependencyMap.containsKey(SOURCE_ANNO_PATH));
                return dependencyMap;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import static org.junit.Assert.assertEquals;
        
        import org.junit.BeforeClass;
        import org.junit.Test;
        
        import java.net.URISyntaxException;
        import java.nio.file.Path;
        import java.nio.file.Paths;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
        
        public class JavaSourceAnalyzerTest {
            private static List mJavaSourceDataList;
        
            private static final String SOURCES_RSP_PATH =
                    "tests/res/testfiles/sources.rsp";
        
            @BeforeClass
            public static void beforeClass() throws URISyntaxException {
                Path path = Paths.get(SOURCES_RSP_PATH);
                // Perform source analysis.
                mJavaSourceDataList = JavaSourceAnalyzer.analyze(path);
            }
        
            @Test
            public void validateSourceData() {
                Map expectedSourceData = expectedSourceData();
                int expectedFileCount = expectedSourceData.size();
                int actualFileCount = 0;
                for (JavaSourceData javaSourceData : mJavaSourceDataList) {
                    String file =  javaSourceData.getFilePath();
                    if (expectedSourceData.containsKey(file)) {
                        actualFileCount++;
                        assertEquals("Source Data not generated correctly for " + file,
                                expectedSourceData.get(file), javaSourceData.getPackagePrependedFileName());
                    }
                }
                assertEquals("Not all source files processed", expectedFileCount, actualFileCount);
            }
        
            private Map expectedSourceData() {
                Map expectedSourceData = new HashMap<>();
                expectedSourceData.put("tests/res/testdata/annotation/AnnotationUsage.java",
                        "res.testdata.annotation.AnnotationUsage.java");
                expectedSourceData.put("tests/res/testdata/constants/ConstantUsage.java",
                        "res.testdata.constants.ConstantUsage.java");
                expectedSourceData.put("tests/res/testdata/inheritance/BaseClass.java",
                        "res.testdata.inheritance.BaseClass.java");
                expectedSourceData.put("tests/res/testdata/methods/FieldUsage.java",
                        "res.testdata.methods.FieldUsage.java");
                return expectedSourceData;
            }
        }
        
        
        ================================================
        FILE: tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java
        ================================================
        /*
         * Copyright (C) 2025 The Android Open Source Project
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         */
        package com.android.dependencymapper;
        
        import org.junit.Test;
        
        import static org.junit.Assert.assertEquals;
        
        import com.android.dependencymapper.Utils;
        
        public class UtilsTest {
        
            @Test
            public void testTrimAndConvertToPackageBasedPath() {
                String testPath1 = "com/android/storage/StorageManager.class";
                String testPath2 = "com/android/package/PackageManager$Package.class";
        
                String expectedPackageBasedPath1 = "com.android.storage.StorageManager";
                String expectedPackageBasedPath2 = "com.android.package.PackageManager$Package";
        
                assertEquals("Package Based Path not constructed correctly",
                        expectedPackageBasedPath1, Utils.trimAndConvertToPackageBasedPath(testPath1));
                assertEquals("Package Based Path not constructed correctly",
                        expectedPackageBasedPath2, Utils.trimAndConvertToPackageBasedPath(testPath2));
            }
        
            @Test
            public void testBuildPackagePrependedClassSource() {
                String qualifiedClassPath1 = "com.android.storage.StorageManager";
                String sourcePath1 = "StorageManager.java";
                String qualifiedClassPath2 = "com.android.package.PackageManager$Package";
                String sourcePath2 = "PackageManager.java";
                String qualifiedClassPath3 = "com.android.storage.StorageManager$Storage";
                String sourcePath3 = "StorageManager$Storage.java";
        
        
                String expectedPackagePrependedPath1 = "com.android.storage.StorageManager.java";
                String expectedPackagePrependedPath2 = "com.android.package.PackageManager.java";
                String expectedPackagePrependedPath3 = "com.android.storage.StorageManager$Storage.java";
        
                assertEquals("Package Prepended Class Source not constructed correctly",
                        expectedPackagePrependedPath1,
                        Utils.buildPackagePrependedClassSource(qualifiedClassPath1, sourcePath1));
                assertEquals("Package Prepended Class Source not constructed correctly",
                        expectedPackagePrependedPath2,
                        Utils.buildPackagePrependedClassSource(qualifiedClassPath2, sourcePath2));
                assertEquals("Package Prepended Class Source not constructed correctly",
                        expectedPackagePrependedPath3,
                        Utils.buildPackagePrependedClassSource(qualifiedClassPath3, sourcePath3));
            }
        }
        
        
        ================================================
        FILE: tools/docker/.gitignore
        ================================================
        gitconfig
        
        
        ================================================
        FILE: tools/docker/Dockerfile
        ================================================
        FROM ubuntu:14.04
        ARG userid
        ARG groupid
        ARG username
        
        RUN apt-get update && apt-get install -y git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip python openjdk-7-jdk
        
        RUN curl -o jdk8.tgz https://android.googlesource.com/platform/prebuilts/jdk/jdk8/+archive/master.tar.gz \
         && tar -zxf jdk8.tgz linux-x86 \
         && mv linux-x86 /usr/lib/jvm/java-8-openjdk-amd64 \
         && rm -rf jdk8.tgz
        
        RUN curl -o /usr/local/bin/repo https://storage.googleapis.com/git-repo-downloads/repo \
         && echo "d06f33115aea44e583c8669375b35aad397176a411de3461897444d247b6c220  /usr/local/bin/repo" | sha256sum --strict -c - \
         && chmod a+x /usr/local/bin/repo
        
        RUN groupadd -g $groupid $username \
         && useradd -m -u $userid -g $groupid $username \
         && echo $username >/root/username \
         && echo "export USER="$username >>/home/$username/.gitconfig
        COPY gitconfig /home/$username/.gitconfig
        RUN chown $userid:$groupid /home/$username/.gitconfig
        ENV HOME=/home/$username
        ENV USER=$username
        
        ENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i
        
        
        ================================================
        FILE: tools/docker/README.md
        ================================================
        The Dockerfile in this directory sets up an Ubuntu Trusty image ready to build
        a variety of Android branches (>= Lollipop). It's particulary useful to build
        older branches that required 14.04 if you've upgraded to something newer.
        
        First, build the image:
        ```
        # Copy your host gitconfig, or create a stripped down version
        $ cp ~/.gitconfig gitconfig
        $ docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t android-build-trusty .
        ```
        
        Then you can start up new instances with:
        ```
        $ docker run -it --rm -v $ANDROID_BUILD_TOP:/src android-build-trusty
        > cd /src; source build/envsetup.sh
        > lunch aosp_arm-eng
        > m -j50
        ```
        
        
        ================================================
        FILE: tools/droiddoc/Android.bp
        ================================================
        // Copyright (C) 2013 The Android Open Source Project
        //
        // Licensed under the Apache License, Version 2.0 (the "License");
        // you may not use this file except in compliance with the License.
        // You may obtain a copy of the License at
        //
        //      http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS,
        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        // See the License for the specific language governing permissions and
        // limitations under the License.
        
        package {
            // See: http://go/android-license-faq
            default_applicable_licenses: [
                "Android-Apache-2.0",
                "build_make_tools_droiddoc_license",
            ],
        }
        
        license {
            name: "build_make_tools_droiddoc_license",
            package_name: "Android Droiddoc Templates",
            license_kinds: [
                "SPDX-license-identifier-BSD",
                "SPDX-license-identifier-CC-BY-2.5",
                "SPDX-license-identifier-GPL-3.0",
                "SPDX-license-identifier-MIT",
            ],
            license_text: ["LICENSE"],
        }
        
        droiddoc_exported_dir {
            name: "droiddoc-templates-pdk",
            path: "templates-pdk",
        }
        
        
        ================================================
        FILE: tools/droiddoc/LICENSE
        ================================================
        -----------------------------------------------------
        microtemplate.js
        
        // Simple JavaScript Templating
        // John Resig - http://ejohn.org/ - MIT Licensed
        
        -----------------------------------------------------
        jquery-history.js
        
        /**
         * jQuery history event v0.1
         * Copyright (c) 2008 Tom Rodenberg 
         * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.
         */
        
                            GNU GENERAL PUBLIC LICENSE
                               Version 3, 29 June 2007
        
         Copyright (C) 2007 Free Software Foundation, Inc. 
         Everyone is permitted to copy and distribute verbatim copies
         of this license document, but changing it is not allowed.
        
                                    Preamble
        
          The GNU General Public License is a free, copyleft license for
        software and other kinds of works.
        
          The licenses for most software and other practical works are designed
        to take away your freedom to share and change the works.  By contrast,
        the GNU General Public License is intended to guarantee your freedom to
        share and change all versions of a program--to make sure it remains free
        software for all its users.  We, the Free Software Foundation, use the
        GNU General Public License for most of our software; it applies also to
        any other work released this way by its authors.  You can apply it to
        your programs, too.
        
          When we speak of free software, we are referring to freedom, not
        price.  Our General Public Licenses are designed to make sure that you
        have the freedom to distribute copies of free software (and charge for
        them if you wish), that you receive source code or can get it if you
        want it, that you can change the software or use pieces of it in new
        free programs, and that you know you can do these things.
        
          To protect your rights, we need to prevent others from denying you
        these rights or asking you to surrender the rights.  Therefore, you have
        certain responsibilities if you distribute copies of the software, or if
        you modify it: responsibilities to respect the freedom of others.
        
          For example, if you distribute copies of such a program, whether
        gratis or for a fee, you must pass on to the recipients the same
        freedoms that you received.  You must make sure that they, too, receive
        or can get the source code.  And you must show them these terms so they
        know their rights.
        
          Developers that use the GNU GPL protect your rights with two steps:
        (1) assert copyright on the software, and (2) offer you this License
        giving you legal permission to copy, distribute and/or modify it.
        
          For the developers' and authors' protection, the GPL clearly explains
        that there is no warranty for this free software.  For both users' and
        authors' sake, the GPL requires that modified versions be marked as
        changed, so that their problems will not be attributed erroneously to
        authors of previous versions.
        
          Some devices are designed to deny users access to install or run
        modified versions of the software inside them, although the manufacturer
        can do so.  This is fundamentally incompatible with the aim of
        protecting users' freedom to change the software.  The systematic
        pattern of such abuse occurs in the area of products for individuals to
        use, which is precisely where it is most unacceptable.  Therefore, we
        have designed this version of the GPL to prohibit the practice for those
        products.  If such problems arise substantially in other domains, we
        stand ready to extend this provision to those domains in future versions
        of the GPL, as needed to protect the freedom of users.
        
          Finally, every program is threatened constantly by software patents.
        States should not allow patents to restrict development and use of
        software on general-purpose computers, but in those that do, we wish to
        avoid the special danger that patents applied to a free program could
        make it effectively proprietary.  To prevent this, the GPL assures that
        patents cannot be used to render the program non-free.
        
          The precise terms and conditions for copying, distribution and
        modification follow.
        
                               TERMS AND CONDITIONS
        
          0. Definitions.
        
          "This License" refers to version 3 of the GNU General Public License.
        
          "Copyright" also means copyright-like laws that apply to other kinds of
        works, such as semiconductor masks.
        
          "The Program" refers to any copyrightable work licensed under this
        License.  Each licensee is addressed as "you".  "Licensees" and
        "recipients" may be individuals or organizations.
        
          To "modify" a work means to copy from or adapt all or part of the work
        in a fashion requiring copyright permission, other than the making of an
        exact copy.  The resulting work is called a "modified version" of the
        earlier work or a work "based on" the earlier work.
        
          A "covered work" means either the unmodified Program or a work based
        on the Program.
        
          To "propagate" a work means to do anything with it that, without
        permission, would make you directly or secondarily liable for
        infringement under applicable copyright law, except executing it on a
        computer or modifying a private copy.  Propagation includes copying,
        distribution (with or without modification), making available to the
        public, and in some countries other activities as well.
        
          To "convey" a work means any kind of propagation that enables other
        parties to make or receive copies.  Mere interaction with a user through
        a computer network, with no transfer of a copy, is not conveying.
        
          An interactive user interface displays "Appropriate Legal Notices"
        to the extent that it includes a convenient and prominently visible
        feature that (1) displays an appropriate copyright notice, and (2)
        tells the user that there is no warranty for the work (except to the
        extent that warranties are provided), that licensees may convey the
        work under this License, and how to view a copy of this License.  If
        the interface presents a list of user commands or options, such as a
        menu, a prominent item in the list meets this criterion.
        
          1. Source Code.
        
          The "source code" for a work means the preferred form of the work
        for making modifications to it.  "Object code" means any non-source
        form of a work.
        
          A "Standard Interface" means an interface that either is an official
        standard defined by a recognized standards body, or, in the case of
        interfaces specified for a particular programming language, one that
        is widely used among developers working in that language.
        
          The "System Libraries" of an executable work include anything, other
        than the work as a whole, that (a) is included in the normal form of
        packaging a Major Component, but which is not part of that Major
        Component, and (b) serves only to enable use of the work with that
        Major Component, or to implement a Standard Interface for which an
        implementation is available to the public in source code form.  A
        "Major Component", in this context, means a major essential component
        (kernel, window system, and so on) of the specific operating system
        (if any) on which the executable work runs, or a compiler used to
        produce the work, or an object code interpreter used to run it.
        
          The "Corresponding Source" for a work in object code form means all
        the source code needed to generate, install, and (for an executable
        work) run the object code and to modify the work, including scripts to
        control those activities.  However, it does not include the work's
        System Libraries, or general-purpose tools or generally available free
        programs which are used unmodified in performing those activities but
        which are not part of the work.  For example, Corresponding Source
        includes interface definition files associated with source files for
        the work, and the source code for shared libraries and dynamically
        linked subprograms that the work is specifically designed to require,
        such as by intimate data communication or control flow between those
        subprograms and other parts of the work.
        
          The Corresponding Source need not include anything that users
        can regenerate automatically from other parts of the Corresponding
        Source.
        
          The Corresponding Source for a work in source code form is that
        same work.
        
          2. Basic Permissions.
        
          All rights granted under this License are granted for the term of
        copyright on the Program, and are irrevocable provided the stated
        conditions are met.  This License explicitly affirms your unlimited
        permission to run the unmodified Program.  The output from running a
        covered work is covered by this License only if the output, given its
        content, constitutes a covered work.  This License acknowledges your
        rights of fair use or other equivalent, as provided by copyright law.
        
          You may make, run and propagate covered works that you do not
        convey, without conditions so long as your license otherwise remains
        in force.  You may convey covered works to others for the sole purpose
        of having them make modifications exclusively for you, or provide you
        with facilities for running those works, provided that you comply with
        the terms of this License in conveying all material for which you do
        not control copyright.  Those thus making or running the covered works
        for you must do so exclusively on your behalf, under your direction
        and control, on terms that prohibit them from making any copies of
        your copyrighted material outside their relationship with you.
        
          Conveying under any other circumstances is permitted solely under
        the conditions stated below.  Sublicensing is not allowed; section 10
        makes it unnecessary.
        
          3. Protecting Users' Legal Rights From Anti-Circumvention Law.
        
          No covered work shall be deemed part of an effective technological
        measure under any applicable law fulfilling obligations under article
        11 of the WIPO copyright treaty adopted on 20 December 1996, or
        similar laws prohibiting or restricting circumvention of such
        measures.
        
          When you convey a covered work, you waive any legal power to forbid
        circumvention of technological measures to the extent such circumvention
        is effected by exercising rights under this License with respect to
        the covered work, and you disclaim any intention to limit operation or
        modification of the work as a means of enforcing, against the work's
        users, your or third parties' legal rights to forbid circumvention of
        technological measures.
        
          4. Conveying Verbatim Copies.
        
          You may convey verbatim copies of the Program's source code as you
        receive it, in any medium, provided that you conspicuously and
        appropriately publish on each copy an appropriate copyright notice;
        keep intact all notices stating that this License and any
        non-permissive terms added in accord with section 7 apply to the code;
        keep intact all notices of the absence of any warranty; and give all
        recipients a copy of this License along with the Program.
        
          You may charge any price or no price for each copy that you convey,
        and you may offer support or warranty protection for a fee.
        
          5. Conveying Modified Source Versions.
        
          You may convey a work based on the Program, or the modifications to
        produce it from the Program, in the form of source code under the
        terms of section 4, provided that you also meet all of these conditions:
        
            a) The work must carry prominent notices stating that you modified
            it, and giving a relevant date.
        
            b) The work must carry prominent notices stating that it is
            released under this License and any conditions added under section
            7.  This requirement modifies the requirement in section 4 to
            "keep intact all notices".
        
            c) You must license the entire work, as a whole, under this
            License to anyone who comes into possession of a copy.  This
            License will therefore apply, along with any applicable section 7
            additional terms, to the whole of the work, and all its parts,
            regardless of how they are packaged.  This License gives no
            permission to license the work in any other way, but it does not
            invalidate such permission if you have separately received it.
        
            d) If the work has interactive user interfaces, each must display
            Appropriate Legal Notices; however, if the Program has interactive
            interfaces that do not display Appropriate Legal Notices, your
            work need not make them do so.
        
          A compilation of a covered work with other separate and independent
        works, which are not by their nature extensions of the covered work,
        and which are not combined with it such as to form a larger program,
        in or on a volume of a storage or distribution medium, is called an
        "aggregate" if the compilation and its resulting copyright are not
        used to limit the access or legal rights of the compilation's users
        beyond what the individual works permit.  Inclusion of a covered work
        in an aggregate does not cause this License to apply to the other
        parts of the aggregate.
        
          6. Conveying Non-Source Forms.
        
          You may convey a covered work in object code form under the terms
        of sections 4 and 5, provided that you also convey the
        machine-readable Corresponding Source under the terms of this License,
        in one of these ways:
        
            a) Convey the object code in, or embodied in, a physical product
            (including a physical distribution medium), accompanied by the
            Corresponding Source fixed on a durable physical medium
            customarily used for software interchange.
        
            b) Convey the object code in, or embodied in, a physical product
            (including a physical distribution medium), accompanied by a
            written offer, valid for at least three years and valid for as
            long as you offer spare parts or customer support for that product
            model, to give anyone who possesses the object code either (1) a
            copy of the Corresponding Source for all the software in the
            product that is covered by this License, on a durable physical
            medium customarily used for software interchange, for a price no
            more than your reasonable cost of physically performing this
            conveying of source, or (2) access to copy the
            Corresponding Source from a network server at no charge.
        
            c) Convey individual copies of the object code with a copy of the
            written offer to provide the Corresponding Source.  This
            alternative is allowed only occasionally and noncommercially, and
            only if you received the object code with such an offer, in accord
            with subsection 6b.
        
            d) Convey the object code by offering access from a designated
            place (gratis or for a charge), and offer equivalent access to the
            Corresponding Source in the same way through the same place at no
            further charge.  You need not require recipients to copy the
            Corresponding Source along with the object code.  If the place to
            copy the object code is a network server, the Corresponding Source
            may be on a different server (operated by you or a third party)
            that supports equivalent copying facilities, provided you maintain
            clear directions next to the object code saying where to find the
            Corresponding Source.  Regardless of what server hosts the
            Corresponding Source, you remain obligated to ensure that it is
            available for as long as needed to satisfy these requirements.
        
            e) Convey the object code using peer-to-peer transmission, provided
            you inform other peers where the object code and Corresponding
            Source of the work are being offered to the general public at no
            charge under subsection 6d.
        
          A separable portion of the object code, whose source code is excluded
        from the Corresponding Source as a System Library, need not be
        included in conveying the object code work.
        
          A "User Product" is either (1) a "consumer product", which means any
        tangible personal property which is normally used for personal, family,
        or household purposes, or (2) anything designed or sold for incorporation
        into a dwelling.  In determining whether a product is a consumer product,
        doubtful cases shall be resolved in favor of coverage.  For a particular
        product received by a particular user, "normally used" refers to a
        typical or common use of that class of product, regardless of the status
        of the particular user or of the way in which the particular user
        actually uses, or expects or is expected to use, the product.  A product
        is a consumer product regardless of whether the product has substantial
        commercial, industrial or non-consumer uses, unless such uses represent
        the only significant mode of use of the product.
        
          "Installation Information" for a User Product means any methods,
        procedures, authorization keys, or other information required to install
        and execute modified versions of a covered work in that User Product from
        a modified version of its Corresponding Source.  The information must
        suffice to ensure that the continued functioning of the modified object
        code is in no case prevented or interfered with solely because
        modification has been made.
        
          If you convey an object code work under this section in, or with, or
        specifically for use in, a User Product, and the conveying occurs as
        part of a transaction in which the right of possession and use of the
        User Product is transferred to the recipient in perpetuity or for a
        fixed term (regardless of how the transaction is characterized), the
        Corresponding Source conveyed under this section must be accompanied
        by the Installation Information.  But this requirement does not apply
        if neither you nor any third party retains the ability to install
        modified object code on the User Product (for example, the work has
        been installed in ROM).
        
          The requirement to provide Installation Information does not include a
        requirement to continue to provide support service, warranty, or updates
        for a work that has been modified or installed by the recipient, or for
        the User Product in which it has been modified or installed.  Access to a
        network may be denied when the modification itself materially and
        adversely affects the operation of the network or violates the rules and
        protocols for communication across the network.
        
          Corresponding Source conveyed, and Installation Information provided,
        in accord with this section must be in a format that is publicly
        documented (and with an implementation available to the public in
        source code form), and must require no special password or key for
        unpacking, reading or copying.
        
          7. Additional Terms.
        
          "Additional permissions" are terms that supplement the terms of this
        License by making exceptions from one or more of its conditions.
        Additional permissions that are applicable to the entire Program shall
        be treated as though they were included in this License, to the extent
        that they are valid under applicable law.  If additional permissions
        apply only to part of the Program, that part may be used separately
        under those permissions, but the entire Program remains governed by
        this License without regard to the additional permissions.
        
          When you convey a copy of a covered work, you may at your option
        remove any additional permissions from that copy, or from any part of
        it.  (Additional permissions may be written to require their own
        removal in certain cases when you modify the work.)  You may place
        additional permissions on material, added by you to a covered work,
        for which you have or can give appropriate copyright permission.
        
          Notwithstanding any other provision of this License, for material you
        add to a covered work, you may (if authorized by the copyright holders of
        that material) supplement the terms of this License with terms:
        
            a) Disclaiming warranty or limiting liability differently from the
            terms of sections 15 and 16 of this License; or
        
            b) Requiring preservation of specified reasonable legal notices or
            author attributions in that material or in the Appropriate Legal
            Notices displayed by works containing it; or
        
            c) Prohibiting misrepresentation of the origin of that material, or
            requiring that modified versions of such material be marked in
            reasonable ways as different from the original version; or
        
            d) Limiting the use for publicity purposes of names of licensors or
            authors of the material; or
        
            e) Declining to grant rights under trademark law for use of some
            trade names, trademarks, or service marks; or
        
            f) Requiring indemnification of licensors and authors of that
            material by anyone who conveys the material (or modified versions of
            it) with contractual assumptions of liability to the recipient, for
            any liability that these contractual assumptions directly impose on
            those licensors and authors.
        
          All other non-permissive additional terms are considered "further
        restrictions" within the meaning of section 10.  If the Program as you
        received it, or any part of it, contains a notice stating that it is
        governed by this License along with a term that is a further
        restriction, you may remove that term.  If a license document contains
        a further restriction but permits relicensing or conveying under this
        License, you may add to a covered work material governed by the terms
        of that license document, provided that the further restriction does
        not survive such relicensing or conveying.
        
          If you add terms to a covered work in accord with this section, you
        must place, in the relevant source files, a statement of the
        additional terms that apply to those files, or a notice indicating
        where to find the applicable terms.
        
          Additional terms, permissive or non-permissive, may be stated in the
        form of a separately written license, or stated as exceptions;
        the above requirements apply either way.
        
          8. Termination.
        
          You may not propagate or modify a covered work except as expressly
        provided under this License.  Any attempt otherwise to propagate or
        modify it is void, and will automatically terminate your rights under
        this License (including any patent licenses granted under the third
        paragraph of section 11).
        
          However, if you cease all violation of this License, then your
        license from a particular copyright holder is reinstated (a)
        provisionally, unless and until the copyright holder explicitly and
        finally terminates your license, and (b) permanently, if the copyright
        holder fails to notify you of the violation by some reasonable means
        prior to 60 days after the cessation.
        
          Moreover, your license from a particular copyright holder is
        reinstated permanently if the copyright holder notifies you of the
        violation by some reasonable means, this is the first time you have
        received notice of violation of this License (for any work) from that
        copyright holder, and you cure the violation prior to 30 days after
        your receipt of the notice.
        
          Termination of your rights under this section does not terminate the
        licenses of parties who have received copies or rights from you under
        this License.  If your rights have been terminated and not permanently
        reinstated, you do not qualify to receive new licenses for the same
        material under section 10.
        
          9. Acceptance Not Required for Having Copies.
        
          You are not required to accept this License in order to receive or
        run a copy of the Program.  Ancillary propagation of a covered work
        occurring solely as a consequence of using peer-to-peer transmission
        to receive a copy likewise does not require acceptance.  However,
        nothing other than this License grants you permission to propagate or
        modify any covered work.  These actions infringe copyright if you do
        not accept this License.  Therefore, by modifying or propagating a
        covered work, you indicate your acceptance of this License to do so.
        
          10. Automatic Licensing of Downstream Recipients.
        
          Each time you convey a covered work, the recipient automatically
        receives a license from the original licensors, to run, modify and
        propagate that work, subject to this License.  You are not responsible
        for enforcing compliance by third parties with this License.
        
          An "entity transaction" is a transaction transferring control of an
        organization, or substantially all assets of one, or subdividing an
        organization, or merging organizations.  If propagation of a covered
        work results from an entity transaction, each party to that
        transaction who receives a copy of the work also receives whatever
        licenses to the work the party's predecessor in interest had or could
        give under the previous paragraph, plus a right to possession of the
        Corresponding Source of the work from the predecessor in interest, if
        the predecessor has it or can get it with reasonable efforts.
        
          You may not impose any further restrictions on the exercise of the
        rights granted or affirmed under this License.  For example, you may
        not impose a license fee, royalty, or other charge for exercise of
        rights granted under this License, and you may not initiate litigation
        (including a cross-claim or counterclaim in a lawsuit) alleging that
        any patent claim is infringed by making, using, selling, offering for
        sale, or importing the Program or any portion of it.
        
          11. Patents.
        
          A "contributor" is a copyright holder who authorizes use under this
        License of the Program or a work on which the Program is based.  The
        work thus licensed is called the contributor's "contributor version".
        
          A contributor's "essential patent claims" are all patent claims
        owned or controlled by the contributor, whether already acquired or
        hereafter acquired, that would be infringed by some manner, permitted
        by this License, of making, using, or selling its contributor version,
        but do not include claims that would be infringed only as a
        consequence of further modification of the contributor version.  For
        purposes of this definition, "control" includes the right to grant
        patent sublicenses in a manner consistent with the requirements of
        this License.
        
          Each contributor grants you a non-exclusive, worldwide, royalty-free
        patent license under the contributor's essential patent claims, to
        make, use, sell, offer for sale, import and otherwise run, modify and
        propagate the contents of its contributor version.
        
          In the following three paragraphs, a "patent license" is any express
        agreement or commitment, however denominated, not to enforce a patent
        (such as an express permission to practice a patent or covenant not to
        sue for patent infringement).  To "grant" such a patent license to a
        party means to make such an agreement or commitment not to enforce a
        patent against the party.
        
          If you convey a covered work, knowingly relying on a patent license,
        and the Corresponding Source of the work is not available for anyone
        to copy, free of charge and under the terms of this License, through a
        publicly available network server or other readily accessible means,
        then you must either (1) cause the Corresponding Source to be so
        available, or (2) arrange to deprive yourself of the benefit of the
        patent license for this particular work, or (3) arrange, in a manner
        consistent with the requirements of this License, to extend the patent
        license to downstream recipients.  "Knowingly relying" means you have
        actual knowledge that, but for the patent license, your conveying the
        covered work in a country, or your recipient's use of the covered work
        in a country, would infringe one or more identifiable patents in that
        country that you have reason to believe are valid.
        
          If, pursuant to or in connection with a single transaction or
        arrangement, you convey, or propagate by procuring conveyance of, a
        covered work, and grant a patent license to some of the parties
        receiving the covered work authorizing them to use, propagate, modify
        or convey a specific copy of the covered work, then the patent license
        you grant is automatically extended to all recipients of the covered
        work and works based on it.
        
          A patent license is "discriminatory" if it does not include within
        the scope of its coverage, prohibits the exercise of, or is
        conditioned on the non-exercise of one or more of the rights that are
        specifically granted under this License.  You may not convey a covered
        work if you are a party to an arrangement with a third party that is
        in the business of distributing software, under which you make payment
        to the third party based on the extent of your activity of conveying
        the work, and under which the third party grants, to any of the
        parties who would receive the covered work from you, a discriminatory
        patent license (a) in connection with copies of the covered work
        conveyed by you (or copies made from those copies), or (b) primarily
        for and in connection with specific products or compilations that
        contain the covered work, unless you entered into that arrangement,
        or that patent license was granted, prior to 28 March 2007.
        
          Nothing in this License shall be construed as excluding or limiting
        any implied license or other defenses to infringement that may
        otherwise be available to you under applicable patent law.
        
          12. No Surrender of Others' Freedom.
        
          If conditions are imposed on you (whether by court order, agreement or
        otherwise) that contradict the conditions of this License, they do not
        excuse you from the conditions of this License.  If you cannot convey a
        covered work so as to satisfy simultaneously your obligations under this
        License and any other pertinent obligations, then as a consequence you may
        not convey it at all.  For example, if you agree to terms that obligate you
        to collect a royalty for further conveying from those to whom you convey
        the Program, the only way you could satisfy both those terms and this
        License would be to refrain entirely from conveying the Program.
        
          13. Use with the GNU Affero General Public License.
        
          Notwithstanding any other provision of this License, you have
        permission to link or combine any covered work with a work licensed
        under version 3 of the GNU Affero General Public License into a single
        combined work, and to convey the resulting work.  The terms of this
        License will continue to apply to the part which is the covered work,
        but the special requirements of the GNU Affero General Public License,
        section 13, concerning interaction through a network will apply to the
        combination as such.
        
          14. Revised Versions of this License.
        
          The Free Software Foundation may publish revised and/or new versions of
        the GNU General Public License from time to time.  Such new versions will
        be similar in spirit to the present version, but may differ in detail to
        address new problems or concerns.
        
          Each version is given a distinguishing version number.  If the
        Program specifies that a certain numbered version of the GNU General
        Public License "or any later version" applies to it, you have the
        option of following the terms and conditions either of that numbered
        version or of any later version published by the Free Software
        Foundation.  If the Program does not specify a version number of the
        GNU General Public License, you may choose any version ever published
        by the Free Software Foundation.
        
          If the Program specifies that a proxy can decide which future
        versions of the GNU General Public License can be used, that proxy's
        public statement of acceptance of a version permanently authorizes you
        to choose that version for the Program.
        
          Later license versions may give you additional or different
        permissions.  However, no additional obligations are imposed on any
        author or copyright holder as a result of your choosing to follow a
        later version.
        
          15. Disclaimer of Warranty.
        
          THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
        APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
        HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
        OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
        THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
        IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
        ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
        
          16. Limitation of Liability.
        
          IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
        WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
        THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
        GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
        USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
        DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
        PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
        EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
        SUCH DAMAGES.
        
          17. Interpretation of Sections 15 and 16.
        
          If the disclaimer of warranty and limitation of liability provided
        above cannot be given local legal effect according to their terms,
        reviewing courts shall apply local law that most closely approximates
        an absolute waiver of all civil liability in connection with the
        Program, unless a warranty or assumption of liability accompanies a
        copy of the Program in return for a fee.
        
                             END OF TERMS AND CONDITIONS
        
                    How to Apply These Terms to Your New Programs
        
          If you develop a new program, and you want it to be of the greatest
        possible use to the public, the best way to achieve this is to make it
        free software which everyone can redistribute and change under these terms.
        
          To do so, attach the following notices to the program.  It is safest
        to attach them to the start of each source file to most effectively
        state the exclusion of warranty; and each file should have at least
        the "copyright" line and a pointer to where the full notice is found.
        
            
            Copyright (C)   
        
            This program is free software: you can redistribute it and/or modify
            it under the terms of the GNU General Public License as published by
            the Free Software Foundation, either version 3 of the License, or
            (at your option) any later version.
        
            This program is distributed in the hope that it will be useful,
            but WITHOUT ANY WARRANTY; without even the implied warranty of
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
            GNU General Public License for more details.
        
            You should have received a copy of the GNU General Public License
            along with this program.  If not, see .
        
        Also add information on how to contact you by electronic and paper mail.
        
          If the program does terminal interaction, make it output a short
        notice like this when it starts in an interactive mode:
        
              Copyright (C)   
            This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
            This is free software, and you are welcome to redistribute it
            under certain conditions; type `show c' for details.
        
        The hypothetical commands `show w' and `show c' should show the appropriate
        parts of the General Public License.  Of course, your program's commands
        might be different; for a GUI interface, you would use an "about box".
        
          You should also get your employer (if you work as a programmer) or school,
        if any, to sign a "copyright disclaimer" for the program, if necessary.
        For more information on this, and how to apply and follow the GNU GPL, see
        .
        
          The GNU General Public License does not permit incorporating your program
        into proprietary programs.  If your program is a subroutine library, you
        may consider it more useful to permit linking proprietary applications with
        the library.  If this is what you want to do, use the GNU Lesser General
        Public License instead of this License.  But first, please read
        .
        
        -----------------------------------------------------
        yui-3.3.0-reset-min.css
        
        /*
        Copyright (c) 2010, Yahoo! Inc. All rights reserved.
        Code licensed under the BSD License:
        http://developer.yahoo.com/yui/license.html
        version: 3.3.0
        build: 3167
        */
        
        
        Software License Agreement (BSD License)
        Copyright (c) 2010, Yahoo! Inc.
        All rights reserved.
        
        Redistribution and use of this software in source and binary forms, with or
        without modification, are permitted provided that the following conditions are
        met:
        
            Redistributions of source code must retain the above copyright notice, this
            list of conditions and the following disclaimer.
            Redistributions in binary form must reproduce the above copyright notice,
            this list of conditions and the following disclaimer in the documentation
            and/or other materials provided with the distribution.
            Neither the name of Yahoo! Inc. nor the names of its contributors may be
            used to endorse or promote products derived from this software without
            specific prior written permission of Yahoo! Inc.
        
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
        ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
        ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
        LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
        ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
        SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        Sources of Intellectual Property Included in the YUI Library
        
        YUI is issued by Yahoo! under the BSD license above. Below is a list of certain
        publicly available software that is the source of intellectual property in YUI,
        along with the licensing terms that pertain to thosesources of IP. This list is
        for informational purposes only and is not intended to represent an exhaustive
        list of third party contributions to the YUI.
        
            Douglas Crockford's JSON parsing and stringifying methods: In the JSON
            Utility, Douglas Crockford's JSON parsing and stringifying methods are
            adapted from work published at JSON.org. The adapted work is in the public
            domain.
        
            Robert Penner's animation-easing algorithms: In the Animation Utility, YUI
            makes use of Robert Penner's algorithms for easing.
        
            Geoff Stearns's SWFObject: In the Charts Control and the Uploader versions
            through 2.7.0, YUI makes use of Geoff Stearns's SWFObject v1.5 for Flash
            Player detection and embedding. More information on SWFObject can be found
            here (http://blog.deconcept.com/swfobject/). SWFObject is (c) 2007 Geoff
            Stearns and is released under the MIT License
            (http://www.opensource.org/licenses/mit-license.php).
        
            Diego Perini's IEContentLoaded technique: The Event Utility employs a
            technique developed by Diego Perini and licensed under GPL. YUI's use of
            this technique is included under our BSD license with the author's
            permission.
        
        
        From MIT license link above:
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
        -----------------------------------------------------
        customizations.cs
        
          Except as noted, this content is 
          licensed under 
          Creative Commons Attribution 2.5.
        
        
        Creative Commons
        Creative Commons Legal Code
        
        Attribution 2.5
        CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL
        SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT
        RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS.
        CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND
        DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
        
        License
        
        THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
        COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
        COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
        AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
        
        BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE
        BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS
        CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
        CONDITIONS.
        
        1. Definitions
        
            "Collective Work" means a work, such as a periodical issue, anthology or
            encyclopedia, in which the Work in its entirety in unmodified form, along
            with a number of other contributions, constituting separate and independent
            works in themselves, are assembled into a collective whole. A work that
            constitutes a Collective Work will not be considered a Derivative Work (as
            defined below) for the purposes of this License.
        
            "Derivative Work" means a work based upon the Work or upon the Work and
            other pre-existing works, such as a translation, musical arrangement,
            dramatization, fictionalization, motion picture version, sound recording,
            art reproduction, abridgment, condensation, or any other form in which the
            Work may be recast, transformed, or adapted, except that a work that
            constitutes a Collective Work will not be considered a Derivative Work for
            the purpose of this License. For the avoidance of doubt, where the Work is
            a musical composition or sound recording, the synchronization of the Work
            in timed-relation with a moving image ("synching") will be considered a
            Derivative Work for the purpose of this License.
        
            "Licensor" means the individual or entity that offers the Work under the
            terms of this License.
        
            "Original Author" means the individual or entity who created the Work.
        
            "Work" means the copyrightable work of authorship offered under the terms
            of this License.
        
            "You" means an individual or entity exercising rights under this License
            who has not previously violated the terms of this License with respect to
            the Work, or who has received express permission from the Licensor to
            exercise rights under this License despite a previous violation.
        
        2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or
        restrict any rights arising from fair use, first sale or other limitations on
        the exclusive rights of the copyright owner under copyright law or other
        applicable laws.
        
        3. License Grant. Subject to the terms and conditions of this License, Licensor
        hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the
        duration of the applicable copyright) license to exercise the rights in the
        Work as stated below:
        
            to reproduce the Work, to incorporate the Work into one or more Collective
            Works, and to reproduce the Work as incorporated in the Collective Works;
        
            to create and reproduce Derivative Works;
        
            to distribute copies or phonorecords of, display publicly, perform
            publicly, and perform publicly by means of a digital audio transmission the
            Work including as incorporated in Collective Works;
        
            to distribute copies or phonorecords of, display publicly, perform
            publicly, and perform publicly by means of a digital audio transmission
            Derivative Works.
        
            For the avoidance of doubt, where the work is a musical composition:
                Performance Royalties Under Blanket Licenses. Licensor waives the
                exclusive right to collect, whether individually or via a performance
                rights society (e.g. ASCAP, BMI, SESAC), royalties for the public
                performance or public digital performance (e.g. webcast) of the Work.
        
                Mechanical Rights and Statutory Royalties. Licensor waives the
                exclusive right to collect, whether individually or via a music rights
                agency or designated agent (e.g. Harry Fox Agency), royalties for any
                phonorecord You create from the Work ("cover version") and distribute,
                subject to the compulsory license created by 17 USC Section 115 of the
                US Copyright Act (or the equivalent in other jurisdictions).
        
            Webcasting Rights and Statutory Royalties. For the avoidance of doubt,
            where the Work is a sound recording, Licensor waives the exclusive right to
            collect, whether individually or via a performance-rights society (e.g.
            SoundExchange), royalties for the public digital performance (e.g. webcast)
            of the Work, subject to the compulsory license created by 17 USC Section
            114 of the US Copyright Act (or the equivalent in other jurisdictions).
        
        The above rights may be exercised in all media and formats whether now known or
        hereafter devised. The above rights include the right to make such
        modifications as are technically necessary to exercise the rights in other
        media and formats. All rights not expressly granted by Licensor are hereby
        reserved.
        
        4. Restrictions.The license granted in Section 3 above is expressly made
        subject to and limited by the following restrictions:
        
            You may distribute, publicly display, publicly perform, or publicly
            digitally perform the Work only under the terms of this License, and You
            must include a copy of, or the Uniform Resource Identifier for, this
            License with every copy or phonorecord of the Work You distribute, publicly
            display, publicly perform, or publicly digitally perform. You may not offer
            or impose any terms on the Work that alter or restrict the terms of this
            License or the recipients' exercise of the rights granted hereunder. You
            may not sublicense the Work. You must keep intact all notices that refer to
            this License and to the disclaimer of warranties. You may not distribute,
            publicly display, publicly perform, or publicly digitally perform the Work
            with any technological measures that control access or use of the Work in a
            manner inconsistent with the terms of this License Agreement. The above
            applies to the Work as incorporated in a Collective Work, but this does not
            require the Collective Work apart from the Work itself to be made subject
            to the terms of this License. If You create a Collective Work, upon notice
            from any Licensor You must, to the extent practicable, remove from the
            Collective Work any credit as required by clause 4(b), as requested. If You
            create a Derivative Work, upon notice from any Licensor You must, to the
            extent practicable, remove from the Derivative Work any credit as required
            by clause 4(b), as requested.
        
            If you distribute, publicly display, publicly perform, or publicly
            digitally perform the Work or any Derivative Works or Collective Works, You
            must keep intact all copyright notices for the Work and provide, reasonable
            to the medium or means You are utilizing: (i) the name of the Original
            Author (or pseudonym, if applicable) if supplied, and/or (ii) if the
            Original Author and/or Licensor designate another party or parties (e.g. a
            sponsor institute, publishing entity, journal) for attribution in
            Licensor's copyright notice, terms of service or by other reasonable means,
            the name of such party or parties; the title of the Work if supplied; to
            the extent reasonably practicable, the Uniform Resource Identifier, if any,
            that Licensor specifies to be associated with the Work, unless such URI
            does not refer to the copyright notice or licensing information for the
            Work; and in the case of a Derivative Work, a credit identifying the use of
            the Work in the Derivative Work (e.g., "French translation of the Work by
            Original Author," or "Screenplay based on original Work by Original
            Author"). Such credit may be implemented in any reasonable manner;
            provided, however, that in the case of a Derivative Work or Collective
            Work, at a minimum such credit will appear where any other comparable
            authorship credit appears and in a manner at least as prominent as such
            other comparable authorship credit.
        
        5. Representations, Warranties and Disclaimer
        
        UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS
        THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND
        CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING,
        WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A
        PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
        ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
        SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH
        EXCLUSION MAY NOT APPLY TO YOU.
        
        6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN
        NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,
        INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS
        LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE
        POSSIBILITY OF SUCH DAMAGES.
        
        7. Termination
        
            This License and the rights granted hereunder will terminate automatically
            upon any breach by You of the terms of this License. Individuals or
            entities who have received Derivative Works or Collective Works from You
            under this License, however, will not have their licenses terminated
            provided such individuals or entities remain in full compliance with those
            licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
            this License.
        
            Subject to the above terms and conditions, the license granted here is
            perpetual (for the duration of the applicable copyright in the Work).
            Notwithstanding the above, Licensor reserves the right to release the Work
            under different license terms or to stop distributing the Work at any time;
            provided, however that any such election will not serve to withdraw this
            License (or any other license that has been, or is required to be, granted
            under the terms of this License), and this License will continue in full
            force and effect unless terminated as stated above.
        
        8. Miscellaneous
        
            Each time You distribute or publicly digitally perform the Work or a
            Collective Work, the Licensor offers to the recipient a license to the Work
            on the same terms and conditions as the license granted to You under this
            License.
        
            Each time You distribute or publicly digitally perform a Derivative Work,
            Licensor offers to the recipient a license to the original Work on the same
            terms and conditions as the license granted to You under this License.
        
            If any provision of this License is invalid or unenforceable under
            applicable law, it shall not affect the validity or enforceability of the
            remainder of the terms of this License, and without further action by the
            parties to this agreement, such provision shall be reformed to the minimum
            extent necessary to make such provision valid and enforceable.
        
            No term or provision of this License shall be deemed waived and no breach
            consented to unless such waiver or consent shall be in writing and signed
            by the party to be charged with such waiver or consent.
        
            This License constitutes the entire agreement between the parties with
            respect to the Work licensed here. There are no understandings, agreements
            or representations with respect to the Work not specified here. Licensor
            shall not be bound by any additional provisions that may appear in any
            communication from You. This License may not be modified without the mutual
            written agreement of the Licensor and You.
        
        Creative Commons is not a party to this License, and makes no warranty
        whatsoever in connection with the Work. Creative Commons will not be liable to
        You or any party on any legal theory for any damages whatsoever, including
        without limitation any general, special, incidental or consequential damages
        arising in connection to this license. Notwithstanding the foregoing two (2)
        sentences, if Creative Commons has expressly identified itself as the Licensor
        hereunder, it shall have all rights and obligations of Licensor.
        
        Except for the limited purpose of indicating to the public that the Work is
        licensed under the CCPL, neither party will use the trademark "Creative
        Commons" or any related trademark or logo of Creative Commons without the prior
        written consent of Creative Commons. Any permitted use will be in compliance
        with Creative Commons' then-current trademark usage guidelines, as may be
        published on its website or otherwise made available upon request from time to
        time.
        
        Creative Commons may be contacted at https://creativecommons.org/.
        
        -----------------------------------------------------
        jquery-resizable.min.js
        
        /*
         * jQuery JavaScript Library v1.3.2
         * http://jquery.com/
         *
         * Copyright (c) 2009 John Resig
         * Dual licensed under the MIT and GPL licenses.
         * http://docs.jquery.com/License
         *
         * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
         * Revision: 6246
         */
        
        The MIT License (MIT)
        
        Copyright (c) 2009 John Resig
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
        -----------------------------------------------------
        jquery-1.6.2.min.js
        
        /*!
         * jQuery JavaScript Library v1.6.2
         * http://jquery.com/
         *
         * Copyright 2011, John Resig
         * Dual licensed under the MIT or GPL Version 2 licenses.
         * http://jquery.org/license
         *
         * Includes Sizzle.js
         * http://sizzlejs.com/
         * Copyright 2011, The Dojo Foundation
         * Released under the MIT, BSD, and GPL Licenses.
         *
         * Date: Thu Jun 30 14:16:56 2011 -0400
         */
        
        The MIT License (MIT)
        
        Copyright (c) 2011 John Resig, and The Dojo Foundation
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
        
        
        ================================================
        FILE: tools/droiddoc/README
        ================================================
        If you're looking for the templates-sdk/ files, they've moved
        to external/doclava/res/assets/.
        
        The remaining template files here should also be eventually removed
        so that we can unify the structure and style of all DocLava builds.
        
        
        ================================================
        FILE: tools/droiddoc/templates-pdk/assets/android-developer-core.css
        ================================================
        /* file: android-developer-core.css
           author: smain
           date: september 2008
           info: core developer styles (developer.android.com)
        */
        
        
        /* RESET STYLES */
        
        html,body,div,h1,h2,h3,h4,h5,h6,p,img,
        dl,dt,dd,ol,ul,li,table,caption,tbody,
        tfoot,thead,tr,th,td,form,fieldset,
        embed,object,applet {
          margin: 0;
          padding: 0;
          border: 0;
        }
        
        /* BASICS */
        
        html, body {
          overflow:hidden; /* keeps scrollbar off IE */
          background-color:#fff;
        }
        
        body {
          font-family:arial,sans-serif;
          color:#000;
          font-size:13px;
          color:#333;
          background-image:url(images/bg_fade.jpg);
          background-repeat:repeat-x;
        }
        
        a, a code {
          color:#006699;
        }
        
        a:active,
        a:active code {
          color:#f00;
        } 
        
        a:visited,
        a:visited code {
          color:#006699;
        }
        
        input, select,
        textarea, option, label {
          font-family:inherit;
          font-size:inherit;
          padding:0;
          margin:0;
          vertical-align:middle;
        }
        
        option {
          padding:0 4px;
        }
        
        p, form {
          padding:0;
          margin:0 0 1em;
        }
        
        code, pre {
          color:#007000;
          font-family:monospace;
          line-height:1em;
        }
        
        var {
          color:#007000;
          font-style:italic;
        }
        
        pre {
          border:1px solid #ccc;
          background-color:#fafafa;
          padding:10px;
          margin:0 0 1em 1em;
          overflow:auto;
          line-height:inherit; /* fixes vertical scrolling in webkit */
        }
        
        h1,h2,h3,h4,h5 {
          margin:1em 0;
          padding:0;
        }
        
        p,ul,ol,dl,dd,dt,li {
          line-height:1.3em;
        }
        
        ul,ol {
          margin:0 0 .8em;
          padding:0 0 0 2em;
        }
        
        li {
          padding:0 0 .5em;
        }
        
        dl {
          margin:0 0 1em 0;
          padding:0;
        }
        
        dt {
          margin:0;
          padding:0;
        }
        
        dd {
          margin:0 0 1em;
          padding:0 0 0 2em;
        }
        
        li p {
          margin:.5em 0 0;
        }
        
        dd p {
          margin:1em 0 0;
        }
        
        li pre, li table, li img {
          margin:.5em 0 0 1em;
        }
        
        dd pre,
        #jd-content dd table,
        #jd-content dd img {
          margin:1em 0 0 1em;
        }
        
        li ul,
        li ol,
        dd ul,
        dd ol {
          margin:0;
          padding: 0 0 0 2em;
        }
        
        li li,
        dd li {
          margin:0;
          padding:.5em 0 0;
        }
        
        dl dl,
        ol dl,
        ul dl {
          margin:0 0 1em;
          padding:0;
        }
        
        table {
          font-size:1em;
          margin:0 0 1em;
          padding:0;
          border-collapse:collapse;
          border-width:0;
          empty-cells:show;
        }
        
        td,th {
          border:1px solid #ccc;
          padding:6px 12px;
          text-align:left;
          vertical-align:top;
          background-color:inherit;
        }
        
        th {
          background-color:#dee8f1;
        }
        
        td > p:last-child {
          margin:0;
        }
        
        hr.blue {
          background-color:#DDF0F2;
          border:none;
          height:5px;
          margin:20px 0 10px;
        }
        
        blockquote {
          margin: 0 0 1em 1em;
          padding: 0 4em 0 1em;
          border-left:2px solid #eee;
        }
        /* LAYOUT */
        
        #body-content {
          /* "Preliminary" watermark for preview releases and interim builds.
          background:transparent url(images/preliminary.png) repeat scroll 0 0; */
          margin:0;
          position:relative;
          width:100%;
        }
        
        #header {
          height: 114px;
          position:relative;
          z-index:100;
          min-width:675px; /* min width for the tabs, before they wrap */
          padding:0 10px;
          border-bottom:3px solid #94b922;
        }
        
        #headerLeft{
          padding: 25px 0 0;
        }
        
        #headerLeft img{
          height:50px;
        }
        
        #headerRight {
          position:absolute;
          right:0;
          top:0;
          text-align:right;
        }
        
        /* Tabs in the header */
        
        #header ul {
          list-style: none;
          margin: 7px 0 0;
          padding: 0;
          height: 29px;
        }
        
        #header li {
          float: left;
          margin: 0px 2px 0px 0px;
          padding:0;
        }
        
        #header li a {
          text-decoration: none;
          display: block;
          background-image: url(images/bg_images_sprite.png);
          background-position: 0 -58px;
          background-repeat: no-repeat;
          color: #666;
          font-size: 13px;
          font-weight: bold;
          width: 94px;
          height: 29px;
          text-align: center;
          margin: 0px;
        }
        
        #header li a:hover {
          background-image: url(images/bg_images_sprite.png);
          background-position: 0 -29px;
          background-repeat: no-repeat;
        }
        
        #header li a span {
          position:relative;
          top:7px;
        }
        
        #header li a span+span {
          display:none;
        }
        
        /* tab highlighting */
        
        .home #home-link a,
        .guide #guide-link a,
        .reference #reference-link a,
        .sdk #sdk-link a,
        .resources #resources-link a,
        .videos #videos-link a {
          background-image: url(images/bg_images_sprite.png);
          background-position: 0 0;
          background-repeat: no-repeat;
          color: #fff;
          font-weight: bold;
          cursor:default;
        }
        
        .home #home-link a:hover,
        .guide #guide-link a:hover,
        .reference #reference-link a:hover,
        .sdk #sdk-link a:hover,
        .resources #resources-link a:hover,
        .videos #videos-link  a:hover {
          background-image: url(images/bg_images_sprite.png);
          background-position: 0 0;
        }
        
        #headerLinks {
          margin:10px 10px 0 0;
          height:13px;
          font-size: 11px;
          vertical-align: top;
        }
        
        #headerLinks a {
          color: #7FA9B5;
        }
        
        #headerLinks img {
          vertical-align:middle;
        }
        
        #language {
          margin:0 10px 0 4px;
        }
        
        #search {
          height:45px;
          margin:15px 10px 0 0;
        }
        
        /* MAIN BODY */
        
        #mainBodyFluid {
          margin: 20px 10px;
          color:#333;
        }
        
        #mainBodyFixed {
          margin: 20px 10px;
          color: #333;
          width:930px;
          position:relative;
        }
        
        #mainBodyFixed h3,
        #mainBodyFluid h3 {
          color:#336666;
          font-size:1.25em;
          margin: 0em 0em 0em 0em;
          padding-bottom:.5em;
        }
        
        #mainBodyFixed h2,
        #mainBodyFluid h2 {
          color:#336666;
          font-size:1.25em;
          margin: 0;
          padding-bottom:.5em;
        }
        
        #mainBodyFixed h1,
        #mainBodyFluid h1 {
          color:#435A6E;
          font-size:1.7em;
          margin: 1em 0;
        }
        
        #mainBodyFixed .green,
        #mainBodyFluid .green,
        #jd-content .green {
          color:#7BB026;
          background-color:none;
        }
        
        #mainBodyLeft {
          float: left;
          width: 600px;
          margin-right: 20px;
          color: #333;
          position:relative;
        }
        
        div.indent {
          margin-left: 40px;
          margin-right: 70px;
        }
        
        #mainBodyLeft p {
          color: #333;
          font-size: 13px;
        }
        
        #mainBodyLeft p.blue {
          color: #669999;
        }
        
        #mainBodyLeft #communityDiv {
          float: left;
          background-image:url(images/bg_community_leftDiv.jpg);
          background-repeat: no-repeat;
          width: 581px;
          height: 347px;
          padding: 20px 0px 0px 20px;
        }
        
        #mainBodyRight {
          float: left;
          width: 300px;
          color: #333;
        }
        
        #mainBodyRight p {
          padding-right: 50px;
          color: #333;
        }
        
        #mainBodyRight table {
          width: 100%;
        }
        
        #mainBodyRight td {
          border:0px solid #666;
          padding:0px 5px;
          text-align:left;
        }
        
        #mainBodyRight td p {
          margin:0 0 1em 0;
        }
        
        #mainBodyRight .blueBorderBox {
          border:5px solid #ddf0f2;
          padding:18px 18px 18px 18px;
          text-align:left;
        }
        
        #mainBodyFixed .seperator {
          background-image:url(images/hr_gray_side.jpg);
          background-repeat:no-repeat;
          width: 100%;
          float: left;
          clear: both;
        }
        
        #mainBodyBottom {
          float: left;
          width: 100%;
          clear:both;
          color: #333;
        }
        
        #mainBodyBottom .seperator {
          background-image:url(images/hr_gray_main.jpg);
          background-repeat:no-repeat;
          width: 100%;
          float: left;
          clear: both;
        }
        
        /* FOOTER */
        
        #footer {
          float: left;
          width:90%;
          margin: 20px;
          color: #aaa;
          font-size: 11px;
        }
        
        #footer a {
          color: #aaa;
          font-size: 11px;
        }
        
        #footer a:hover {
          text-decoration: underline;
          color:#aaa;
        }
        
        #footerlinks {
          margin-top:2px;
        }
        
        #footerlinks a,
        #footerlinks a:visited {
          color:#006699;
        }
        
        /* SEARCH FILTER */
        
        #search_autocomplete {
          color:#aaa;
        }
        
        #search-button {
          display:inline;
        }
        
        #search_filtered_div {
          position:absolute;
          margin-top:-1px;
          z-index:101;
          border:1px solid #BCCDF0;
          background-color:#fff;
        }
        
        #search_filtered {
          min-width:100%;
        }
        #search_filtered td{
          background-color:#fff;
          border-bottom: 1px solid #669999;
          line-height:1.5em;
        }
        
        #search_filtered .jd-selected {
          background-color: #94b922;
          cursor:pointer;
        }
        #search_filtered .jd-selected,
        #search_filtered .jd-selected a {
          color:#fff;
        }
        
        .no-display {
          display: none;
        }
        
        .jd-autocomplete {
          font-family: Arial, sans-serif;
          padding-left: 6px;
          padding-right: 6px;
          padding-top: 1px;
          padding-bottom: 1px;
          font-size: 0.81em;
          border: none;
          margin: 0;
          line-height: 1.05em;
        }
        
        .show-row {
          display: table-row;
        }
        .hide-row {
          display: hidden;
        }
        
        /* SEARCH */
        
        /* restrict global search form width */
        #searchForm {
          width:350px;
        }
        
        #searchTxt {
          width:200px;
        }
        
        /* disable twiddle and size selectors for left column */
        #leftSearchControl div {
          width: 100%;
        }
        
        #leftSearchControl .gsc-twiddle {
          background-image : none;
        }
        
        #leftSearchControl td, #searchForm td {
          border: 0px solid #000;
        }
        
        #leftSearchControl .gsc-resultsHeader .gsc-title {
          padding-left : 0px;
          font-weight : bold;
          font-size : 13px;
          color:#006699;
          display : none;
        }
        
        #leftSearchControl .gsc-resultsHeader div.gsc-results-selector {
          display : none;
        }
        
        #leftSearchControl .gsc-resultsRoot {
          padding-top : 6px;
        }
        
        #leftSearchControl div.gs-visibleUrl-long {
          display : block;
          color:#006699;
        }
        
        .gsc-webResult div.gs-visibleUrl-short,
        table.gsc-branding,
        .gsc-clear-button {
          display : none;
        }
        
        .gsc-cursor-box .gsc-cursor div.gsc-cursor-page,
        .gsc-cursor-box .gsc-trailing-more-results a.gsc-trailing-more-results,
        #leftSearchControl a,
        #leftSearchControl a b {
          color:#006699;
        }
        
        .gsc-resultsHeader {
          display: none;
        }
        
        /* Disable built in search forms */
        .gsc-control form.gsc-search-box {
          display : none;
        }
        table.gsc-search-box {
          margin:6px 0 0 0;
          border-collapse:collapse;
        }
        
        td.gsc-input {
          padding:0 2px;
          width:100%;
          vertical-align:middle;
        }
        
        input.gsc-input {
          border:1px solid #BCCDF0;
          width:99%;
          padding-left:2px;
          font-size:.95em;
        }
        
        td.gsc-search-button {
          text-align: right;
          padding:0;
          vertical-align:top;
        }
        
        #search-button {
          margin:0 0 0 2px;
          font-size:11px;
        }
        
        /* search result tabs */
        
        #doc-content .gsc-control {
          position:relative;
        }
        
        #doc-content .gsc-tabsArea {
          position:relative;
          white-space:nowrap;
        }
        
        #doc-content .gsc-tabHeader {
          padding: 3px 6px;
          position:relative;
          width:auto;
        }
        
        #doc-content .gsc-tabHeader.gsc-tabhActive {
          border-top: 2px solid #94B922;
        }
        
        #doc-content h2#searchTitle {
          padding:0;
        }
        
        #doc-content .gsc-resultsbox-visible {
          padding:1em 0 0 6px;
        }
        
        /* CAROUSEL */
        
        #homeMiddle {
          padding: 0px 0px 0px 0px;
          float: left;
          width: 584px;
          height: 627px;
          position:relative;
        }
        
        #topAnnouncement {
          background:url(images/home/bg_home_announcement.png) no-repeat 0 0;
        }
          
        #homeTitle {
          padding:15px 15px 0;
          height:30px;
        }
        
        #homeTitle h2 {
          padding:0;
        }
        
        #announcement-block {
          padding:0 15px 0;
          overflow:hidden;
          background: url(images/hr_gray_side.jpg) no-repeat 15px 0;
          zoom:1;
        }
        
        #announcement-block>* {
          padding:15px 0 0;
        }
        
        #announcement-block img {
          float:left;
          margin:0 30px 0 0;
        }
        
        #announcement {
          float:left;
          margin:0;
        }
        
        #carousel {
          background:url(images/home/bg_home_carousel.png) no-repeat 0 0;
          position:relative;
          height:400px;
        }
        
        #carouselMain {
          background: url(images/home/bg_home_carousel_board.png) 0 0 no-repeat;
          height:auto;
          padding: 25px 21px 0;
          overflow:hidden;
          position:relative;
          zoom:1; /*IE6*/
        }
        
        #carouselMain img {
          margin:0;
        }
        
        #carouselMain .bulletinDesc h3 {
          margin:0;
          padding:0;
        }
        
        #carouselMain .bulletinDesc p {
          margin:0;
          padding:0.7em 0 0;
        }
        
        #carouselWheel {
          background: url(images/home/bg_home_carousel_wheel.png) 0 0 no-repeat;
          padding-top:40px;
          height:150px;
        }
        
        .clearer { clear:both; }
        
        a#arrow-left, a#arrow-right {
          float:left;
          width:42px;
          height:42px;
          background-image:url(images/home/carousel_buttons_sprite.png);
          background-repeat:no-repeat;
        }
        a#arrow-left {
          margin:35px 3px 0 10px;
        }
        a#arrow-right {
          margin:35px 10px 0 0;
        }
        a.arrow-left-off,
        a#arrow-left.arrow-left-off:hover {
          background-position:0 0;
        }
        a.arrow-right-off,
        a#arrow-right.arrow-right-off:hover {
          background-position:-42px 0;
        }
        a#arrow-left:hover {
          background-position:0 -42px;
        }
        a#arrow-right:hover {
          background-position:-42px -42px;
        }
        a.arrow-left-on {
          background-position:0 0;
        }
        a.arrow-right-on {
          background-position:-42px 0;
        }
        a.arrow-right-off,
        a.arrow-left-off {
          cursor:default;
        }
        
        .app-list-container {
          margin:0 20px;
          position:relative;
          width:100%;
        }
        
        div#list-clip {
          height:110px;
          width:438px;
          overflow:hidden;
          position:relative;
          float:left;
        }
        
        div#app-list {
          left:0;
          z-index:1;
          position:absolute;
          margin:11px 0 0;
          _margin-top:13px;
          width:1000%;
        }
        
        #app-list a {
          display:block;
          float:left;
          height:90px;
          width:90px;
          margin:0 24px 0;
          padding:3px;
          background:#99cccc;
          -webkit-border-radius:7px;
          -moz-border-radius:7px;
          border-radius:7px;
          text-decoration:none;
          text-align:center;
          font-size:11px;
          line-height:11px;
        }
        
        #app-list a span {
          position:relative;
          top:-4px;
        }
        
        #app-list img {
          width:90px;
          height:70px;
          margin:0;
        }
        
        #app-list a.selected,
        #app-list a:active.selected,
        #app-list a:hover.selected {
          background:#A4C639;
          color:#fff;
          cursor:default;
          text-decoration:none;
        }
        
        #app-list a:hover,
        #app-list a:active {
          background:#ff9900;
        }
        
        #app-list a:hover span,
        #app-list a:active span {
          text-decoration:underline;
        }
        
        #droid-name {
          padding-top:.5em;
          color:#666;
          padding-bottom:.25em;
        }
        
        /*IE6*/
        * html #app-list a { zoom: 1; margin:0 24px 0 15px;}
        
        * html #list-clip {
          width:430px !important;
        }
        
        /*carousel bulletin layouts*/
        /*460px width*/
        /*185px height*/
        .img-left {
          float:left;
          width:230px;
          overflow:hidden;
          padding:8px 0 8px 8px;
        }
        .desc-right {
          float:left;
          width:270px;
          padding:10px;
        }
        .img-right {
          float:right;
          width:220px;
          overflow:hidden;
          padding:8px 8px 8px 0;
        }
        .desc-left {
          float:right;
          width:280px;
          padding:10px;
          text-align:right;
        }
        .img-top {
          padding:20px 20px 0;
        }
        .desc-bottom {
          padding:10px;
        }
        
        
        /* VIDEO PAGE */
        
        #mainBodyLeft.videoPlayer {
          width:570px;
        }
        
        #mainBodyRight.videoPlayer {
          width:330px;
        }
        
        /* player */
        
        #videoPlayerBox {
          background-color: #DAF3FC;
          border-radius:7px;
          -moz-border-radius:7px;
          -webkit-border-radius:7px;
          width:530px;
          padding:20px;
          border:1px solid #d3ecf5;
          box-shadow:2px 3px 1px #eee;
          -moz-box-shadow:2px 3px 1px #eee;
          -webkit-box-shadow:2px 3px 1px #eee;
        }
        
        #videoBorder {
          background-color: #FFF;
          min-height:399px;
          height:auto !important;
          border:1px solid #ccdada;
          border-radius:7px 7px 0 0;
          -moz-border-radius:7px 7px 0 0;
          -webkit-border-top-left-radius:7px;
          -webkit-border-top-right-radius:7px;
        }
        
        #videoPlayerTitle {
          width:500px;
          padding:15px 15px 0;
        }
        
        #videoPlayerTitle h2 {
          font-weight:bold;
          font-size:1.2em;
          color:#336666;
          margin:0;
          padding:0;
        }
        
        #objectWrapper {
          padding:15px 15px;
          height:334px;
          width:500px;
        }
        
        /* playlist tabs */
        
        ul#videoTabs {
          list-style-type:none;
          padding:0;
          clear:both;
          margin:0;
          padding: 20px 0 0 15px;
          zoom:1; /* IE7/8, otherwise top-padding is double */
        }
        
        ul#videoTabs li {
          display:inline;
          padding:0;
          margin:0 3px 0 0;
          line-height:2em;
        }
        
        ul#videoTabs li a {
          border-radius:7px 7px 0 0;
          -moz-border-radius:7px 7px 0 0;
          -webkit-border-top-left-radius:7px;
          -webkit-border-top-right-radius:7px;
          background:#95c0d0;
          color:#fff;
          text-decoration:none;
          padding:.45em 1.5em;
          font-weight:bold;
        }
        
        ul#videoTabs li.selected a {
          font-weight:bold;
          text-decoration:none;
          color:#555;
          background:#daf3fc;
          border-bottom:1px solid #daf3fc;
        }
        
        ul#videoTabs li:hover a {
          background:#85acba;
        }
        
        ul#videoTabs li.selected:hover a {
          background:#daf3fc;
        }
        
        /* playlists */
        
        #videos {
          background:#daf3fc;
          margin-bottom:1.5em;
          padding:15px;
          border-radius:5px;
          -moz-border-radius:5px;
          -webkit-border-radius:5px;
          box-shadow:2px 3px 1px #eee;
          -moz-box-shadow:2px 3px 1px #eee;
          -webkit-box-shadow:2px 3px 1px #eee;
        }
        
        #videos div {
          display:none;
        }
        
        #videos div.selected {
          display:block;
        }
        
        ul.videoPreviews {
          list-style:none;
          padding:0;
          margin:0;
          zoom:1; /* IE, otherwise, layout doesn't update when showing 'more' */
        }
        
        ul.videoPreviews li {
          margin:0 0 5px;
          padding:0;
          overflow:hidden;
          position:relative;
        }
        
        #mainBodyFixed ul.videoPreviews h3 {
          font-size: 12px;
          margin:0 0 1em 130px;
          padding:0;
          font-weight:bold;
          color:inherit;
        }
        
        ul.videoPreviews a {
          margin:1px;
          padding:10px;
          text-decoration:none;
          height:90px;
          display:block;
          border-radius:5px;
          -moz-border-radius:5px;
          -webkit-border-radius:5px;
          background-color:transparent;
        }
        
        ul.videoPreviews a:hover {
          background-color:#FFF;
          border:none; /* IE8, otherwise, bg doesn't work */
        }
        
        ul.videoPreviews a.selected {
          background-color: #FF9900;
        }
        
        ul.videoPreviews img {
          float:left;
          clear:left;
          margin:0;
        }
        
        ul.videoPreviews h3 {
          font-size:12px;
          font-weight:bold;
          text-decoration:none;
          margin:0 0 1em 130px;
          padding:0;
        }
        
        ul.videoPreviews p {
          font-size: 12px;
          text-decoration:none;
          margin:0 0 1.2em 130px;
        }
        
        ul.videoPreviews p.full {
          display:none;
        }
        
        ul.videoPreviews span.more {
          padding:0 0 0 12px;
          background:url(images/arrow_bluelink_down.png) 0 2px no-repeat;
        }
        
        ul.videoPreviews span.less {
          padding:0 0 0 12px;
          background:url(images/arrow_bluelink_up.png) 0 2px no-repeat;
          display:none;
        }
        
        ul.videoPreviews p.toggle {
          position:absolute;
          margin:0;
          margin-top:-23px; /* instead of bottom:23px, because IE won't do it correctly */
          left:140px;
        }
        
        ul.videoPreviews p.toggle a {
          height:auto;
          margin:0;
          padding:0;
          zoom:1; /* IE6, otherwise the margin considers the img on redraws */
        }
        
        ul.videoPreviews p.toggle a:hover {
          text-decoration:underline;
          background:transparent; /* IE6, otherwise it inherits white */
        }
        
        /* featured videos */
        
        #mainBodyRight h2 {
          padding:0 0 5px;
        }
        
        #mainBodyRight ul.videoPreviews {
          margin:10px 0 0;
        }
        
        #mainBodyRight ul.videoPreviews li {
          font-size:11px;
          line-height:13px;
          margin:0 0 5px;
          padding:0;
        }
        
        #mainBodyRight ul.videoPreviews h3 {
          padding:0;
          margin:0;
          font-size:100%;
        }
        
        #mainBodyRight ul.videoPreviews a {
          text-decoration:none;
          height:108px;
          border:1px solid #FFF;
        }
        
        #mainBodyRight ul.videoPreviews a:hover {
          border:1px solid #CCDADA;
        }
        
        #mainBodyRight ul.videoPreviews a.selected {
          border:1px solid #FFF;
        }
        
        #mainBodyRight ul.videoPreviews p {
          line-height:1.2em;
          padding:0;
          margin:4px 0 0 130px;
        }
        
        #mainBodyRight ul.videoPreviews img {
          margin-top:5px;
        }
        
        /* Pretty printing styles. Used with prettify.js. */
        
        .str { color: #080; }
        .kwd { color: #008; }
        .com { color: #800; }
        .typ { color: #606; }
        .lit { color: #066; }
        .pun { color: #660; }
        .pln { color: #000; }
        dl.tag-list dt code,
        .tag { color: #008; }
        dl.atn-list dt code,
        .atn { color: #828; }
        .atv { color: #080; }
        .dec { color: #606; }
        
        @media print {
          .str { color: #060; }
          .kwd { color: #006; font-weight: bold; }
          .com { color: #600; font-style: italic; }
          .typ { color: #404; font-weight: bold; }
          .lit { color: #044; }
          .pun { color: #440; }
          .pln { color: #000; }
          .tag { color: #006; font-weight: bold; }
          .atn { color: #404; }
          .atv { color: #060; }
        }
        
        
        ================================================
        FILE: tools/droiddoc/templates-pdk/assets/android-developer-docs-devguide.css
        ================================================
        
        @import url("android-developer-docs.css");
        
        /* Page title */
        
        #jd-header h1 {
          padding: 8px 0 0 0;
        }
        
        /* Page content container */
        
        #jd-header table {
        margin: 0 0 1em 1em;
        }
        
        #jd-content table table,
        #jd-content table img {
          margin:1em 0;
        }
        
        ================================================
        FILE: tools/droiddoc/templates-pdk/assets/android-developer-docs.css
        ================================================
        /* file: android-developer-docs.css
           author: smain
           date: september 2008
           info: developer doc styles (developer.android.com)
        */
        
        @import url("android-developer-core.css");
        
        #title {
          border-bottom: 4px solid #ccc;
          display:none;
        }
        
        #title h1 {
          color:#336666;
          margin:0;
          padding: 5px 10px;
          font-size: 1em;
          line-height: 15px;
        }
        
        #title h1 .small{
          color:#000;
          margin:0;
          font-size: 13px;
          padding:0 0 0 15px;
        }
        
        /* SIDE NAVIGATION */
        
        #side-nav {
          padding:0 6px 0 0;
          background-color: #fff;
          font-size:12px;
        }
        
        #resize-packages-nav {
        /* keeps the resize handle below the h-scroll handle */
          height:270px;
          overflow:hidden;
          max-height:100%;
        }
        
        #packages-nav {
          height:270px;
          max-height:inherit;
          position:relative;
          overflow:auto;
        }
        
        #classes-nav,
        #devdoc-nav {
          overflow:auto;
          position:relative;
        }
        
        #side-nav ul {
          list-style: none;
          margin: 0;
          padding:5px 0;
        }
        
        #side-nav ul ul {
          margin: .5em 0 0 0;
          padding: 0;
        }
        
        #side-nav li {
          padding:0;
          padding:1px 0 1px 0;
          zoom:1;
        }
        
        #side-nav li span.heading,
        #side-nav li h2 {
          display:block;
          font-size:12px;
          font-weight: bold;
          margin:.5em 0 0 0;
          padding: 3px 0 1px 9px;
        }
        
        #side-nav li a {
          display: inline-block; /* needed to apply padding to line-wraps */
          text-decoration:none;
          padding: 0 0 0 18px;
          zoom:1;
        }
        
        #side-nav li a span+span {
          display:none;
        }
        
        #side-nav li a:hover {
          text-decoration:underline;
        }
        
        #side-nav li a+a {
          padding: 0;
        }
        /*second level (nested) list*/
        #side-nav li li li a {
          padding: 0 0 0 28px;
        }
        /*third level (nested) list*/
        #side-nav li li li li a {
          padding: 0 0 0 38px;
        }
        
        #side-nav .selected {
          background-color: #435a6e;
          color: #fff;
          font-weight:bold;
        }
        
        #side-nav .selected a {
          color: #fff;
          text-decoration:none;
        }
        
        #side-nav strong {
          display:block;
        }
        
        #side-nav .toggle-list .toggle-img {
          margin:0;
          padding:0;
          position:absolute;
          top:0;
          left:0;
          height:16px;
          width:15px;
          outline-style:none;
        }
        /* second-level toggle */
        #side-nav .toggle-list .toggle-list .toggle-img {
          left:10px;
        }
        
        #side-nav .closed .toggle-img,
        #side-nav .open .closed .toggle-img {
          background:url('images/triangle-closed-small.png') 7px 4px no-repeat;
        }
        #side-nav .open .toggle-img {
          background:url('images/triangle-opened-small.png') 7px 4px no-repeat;
        }
        
        #side-nav .toggle-list {
          position:relative;
        }
        
        #side-nav .toggle-list ul {
          margin:0;
          display:none;
        }
        
        #side-nav .toggle-list div {
          display:block;
        }
        
        #index-links .selected {
          background-color: #fff;
          color: #000;
          font-weight:normal;
          text-decoration:none;
        }
        
        #index-links {
          padding:7px 0 4px 10px;
        }
        
        /* nav tree */
        
        #nav-tree ul {
          padding:5px 0 1.5em;
        }
        
        #side-nav #nav-tree ul li a,
        #side-nav #nav-tree ul li span.no-children {
          padding: 0 0 0 0;
          margin: 0;
        }
        
        #nav-tree .plus {
          margin: 0 3px 0 0;
        }
        
        #nav-tree ul ul {
          list-style: none;
          margin: 0;
          padding: 0 0 0 0;
        }
        
        #nav-tree ul li {
          margin: 0;
          padding: 0 0 0 0;
          white-space: nowrap;
        }
        
        #nav-tree .children_ul {
          margin:0;
        }
        
        #nav-tree a.nolink {
          color: black;
          text-decoration: none;
        }
        
        #nav-tree span.label {
          width: 100%;
        }
        
        #nav-tree {
          overflow-x: auto;
          overflow-y: scroll;
        }
        
        #nav-swap {
          font-size:10px;
          line-height:10px;
          margin-left:1em;
          text-decoration:none;
          display:block;
        }
        
        #tree-link {
        
        }
        
        /* DOCUMENT BODY */
        
        #doc-content {
          overflow:auto;
        }
        
        #jd-header {
          background-color: #E2E2E2;
          padding: 7px 15px;
        }
        
        #jd-header h1 {
          margin: 0 0 10px;
          font-size:1.7em;
        }
        
        #jd-header .crumb {
          font-size:.9em;
          line-height:1em;
          color:#777;
        }
        
        #jd-header .crumb a,
        #jd-header .crumb a:visited {
          text-decoration:none;
          color:#777;
        }
        
        #jd-header .crumb a:hover {
          text-decoration:underline;
        }
        
        #jd-header table {
          margin:0;
          padding:0;
        }
        
        #jd-header td {
          border:none;
          padding:0;
          vertical-align:top;
        }
        
        #jd-header.guide-header {
          background-color:#fff;
          color:#435a6e;
          height:50px;
        }
        
        #jd-descr {
          position:relative;
        }
        
        /* summary tables for reference pages */
        .jd-sumtable {
          margin: .5em 1em 1em 1em;
          width:95%; /* consistent table widths; within IE's quirks */
          font-size:.9em;
        }
        
        .jd-sumtable a {
          text-decoration:none;
        }
        
        .jd-sumtable a:hover {
          text-decoration:underline;
        }
        
        /* the link inside a sumtable for "Show All/Hide All" */
        .toggle-all {
          display:block;
          float:right;
          font-weight:normal;
          font-size:0.9em;
        }
        
        /* adjustments for in/direct subclasses tables */
        .jd-sumtable-subclasses {
          margin: 1em 0 0 0;
          max-width:968px;
        }
        
        /* extra space between end of method name and open-paren */
        .sympad {
          margin-right: 2px;
        }
        
        /* right alignment for the return type in sumtable */
        .jd-sumtable .jd-typecol {
          text-align:right;
        }
        
        /* adjustments for the expando table-in-table */
        .jd-sumtable-expando {
          margin:.5em 0;
          padding:0;
        }
        
        /* a div that holds a short description */
        .jd-descrdiv {
          padding:3px 1em 0 1em;
          margin:0;
          border:0;
        }
        
        /* page-top-right container for reference pages (holds
        links to summary tables) */
        #api-info-block {
          font-size:.8em;
          padding:6px 10px;
          font-weight:normal;
          float:right;
          text-align:right;
          color:#999;
          max-width:70%;
        }
        
        #api-level-toggle {
          padding:0 10px;
          font-size:11px;
          float:right;
        }
        
        #api-level-toggle label.disabled {
          color:#999;
        }
        
        div.api-level {
          font-size:.8em;
          font-weight:normal;
          color:#999;
          float:right;
          padding:0 7px 0;
          margin-top:-25px;
        }
        
        #api-info-block div.api-level {
          font-size:1.3em;
          font-weight:bold;
          float:none;
          color:#444;
          padding:0;
          margin:0;
        }
        
        /* Force link colors for IE6 */
        div.api-level a {
          color:#999;
        }
        #api-info-block div.api-level a:link {
          color:#444;
        }
        #api-level-toggle a {
          color:#999;
        }
        
        div#deprecatedSticker {
          display:none;
          z-index:99;
          position:fixed;
          right:15px;
          top:114px;
          margin:0;
          padding:1em;
          background:#FFF;
          border:1px solid #dddd00;
          box-shadow:-5px 5px 10px #ccc;
          -moz-box-shadow:-5px 5px 10px #ccc;
          -webkit-box-shadow:-5px 5px 10px #ccc;
        }
        
        div#naMessage {
          display:none;
          width:555px;
          height:0;
          margin:0 auto;
        }
        
        div#naMessage div {
          z-index:99;
          width:450px;
          position:fixed;
          margin:50px 0;
          padding:4em 4em 3em;
          background:#FFF;
          border:1px solid #dddd00;
          box-shadow:-10px 10px 40px #888;
          -moz-box-shadow:-10px 10px 40px #888;
          -webkit-box-shadow:-10px 10px 40px #888;
        }
        /* IE6 can't position fixed */
        * html div#naMessage div { position:absolute; }
        
        div#naMessage strong {
          font-size:1.1em;
        }
        
        .absent,
        .absent a:link,
        .absent a:visited,
        .absent a:hover,
        .absent * {
          color:#bbb !important;
          cursor:default !important;
          text-decoration:none !important;
        }
        
        #api-level-toggle a,
        .api-level a {
          color:inherit;
          text-decoration:none;
        }
        
        #api-level-toggle a:hover,
        .api-level a:hover {
          color:inherit;
          text-decoration:underline !important;
          cursor:pointer !important;
        }
        
        #side-nav li.absent.selected,
        #side-nav li.absent.selected *,
        #side-nav div.label.absent.selected,
        #side-nav div.label.absent.selected * {
          background-color:#eaeaea !important;
        }
        /* IE6 quirk (won't chain classes, so just keep background blue) */
        * html #side-nav li.selected,
        * html #side-nav li.selected *,
        * html #side-nav div.label.selected,
        * html #side-nav div.label.selected * {
          background-color: #435a6e !important;
        }
        
        
        .absent h4.jd-details-title,
        .absent h4.jd-details-title * {
          background-color:#f6f6f6 !important;
        }
        
        .absent img {
          opacity: .3;
          filter: alpha(opacity=30);
          -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
        }
        
        
        /* applies to a div containing links to summary tables */
        .sum-details-links {
          padding:0;
          font-weight:normal;
        }
        
        .sum-details-links a {
          text-decoration:none;
        }
        
        .sum-details-links a:hover {
          text-decoration:underline;
        }
        
        
        /* inheritance table */
        .jd-inheritance-table {
          border-spacing:0;
          margin:0;
          padding:0;
          font-size:.9em;
        }
        .jd-inheritance-table td {
          border: none;
          margin: 0;
          padding: 0;
        }
        .jd-inheritance-table .jd-inheritance-space {
          font-weight:bold;
          width:1em;
        }
        .jd-inheritance-table .jd-inheritance-interface-cell {
          padding-left: 17px;
        }
        
        #jd-content {
          padding: 18px 15px;
        }
        
        hr {
          background-color:#ccc;
          border-color:#fff;
          margin:2em 0 1em;
        }
        
        /* DOC CLASSES */
        
        #jd-content h1 {
        /*sdk page*/
          font-size:1.6em;
          color:#336666;
          margin:0 0 .5em;
        }
        
        #jd-content h2 {
          font-size:1.45em;
          color:#111;
          border-top:2px solid #ccc;
          padding: .5em 0 0;
          margin: 2em 0 1em 0;
        }
        
        #jd-content h3 {
          font-size:1.3em;
          color:#3a3a3a;
          padding: 0;
          margin: 1.5em 0 .65em 0;
        }
        
        #jd-content h4 {
          font-size:1.1em;
          color:#3a3a3a;
          padding: 0;
          margin: 1.25em 0 .65em 0;
        }
        
        #jd-content h5 {
          font-size:1.0em;
          color:#3a3a3a;
          padding: 0;
          margin: 1em 0 .65em 0;
        }
        
        #jd-content .small-header {
          font-size:1em;
          color:#000;
          font-weight:bold;
          border:none;
          padding:0;
          margin:1em 0 .5em;
          position:inherit;
        }
        
        #jd-content table {
          margin: 0 0 1em 1em;
        }
        
        #jd-content img {
          margin: 0 0 1em 1em;
        }
        
        #jd-content li img,
        #jd-content dd img {
          margin:.5em 0 .5em 1em;
        }
        
        .nolist {
          list-style:none;
          padding:0;
          margin:0 0 1em 1em;
        }
        
        .nolist li {
          padding:0 0 2px;
          margin:0;
        }
        
        h4 .normal {
          font-size:.9em;
          font-weight:normal;
        }
        
        .caps {
          font-variant:small-caps;
          font-size:1.2em;
        }
        
        dl.tag-list dl.atn-list {
          padding:0 0 0 2em;
        }
        
        .jd-details {
        /*  border:1px solid #669999;
          padding:4px; */
          margin:0 0 1em;
        }
        
        /* API reference: a container for the
        .tagdata blocks that make up the detailed
        description */
        .jd-details-descr {
          padding:0;
          margin:.5em .25em;
        }
        
        /* API reference: a block containing
        a detailed description, a params table,
        seealso list, etc */
        .jd-tagdata {
          margin:.5em 1em;
        }
        
        .jd-tagdata p {
          margin:0 0 1em 1em;
        }
        
        /* API reference: adjustments to
        the detailed description block */
        .jd-tagdescr {
          margin:.25em 0 .75em 0;
          line-height:1em;
        }
        
        .jd-tagdescr p {
          margin:.5em 0;
          padding:0;
        
        }
        
        .jd-tagdescr ol,
        .jd-tagdescr ul {
          margin:0 2.5em;
          padding:0;
        }
        
        .jd-tagdescr table,
        .jd-tagdescr img {
          margin:.25em 1em;
        }
        
        .jd-tagdescr li {
        margin:0 0 .25em 0;
        padding:0;
        }
        
        /* API reference: heading marking
        the details section for constants,
        attrs, methods, etc. */
        h4.jd-details-title {
          font-size:1.15em;
          background-color: #E2E2E2;
          margin:1.5em 0 .6em;
          padding:3px 95px 3px 3px; /* room for api-level */
        }
        
        h4.jd-tagtitle {
          margin:0;
        }
        
        /* API reference: heading for "Parameters", "See Also", etc.,
        in details sections */
        h5.jd-tagtitle {
          margin:0 0 .25em 0;
          font-size:1em;
        }
        
        .jd-tagtable {
          margin:0;
        }
        
        .jd-tagtable td,
        .jd-tagtable th {
          border:none;
          background-color:#fff;
          vertical-align:top;
          font-weight:normal;
          padding:2px 10px;
        }
        
        .jd-tagtable th {
          font-style:italic;
        }
        
        #jd-content table h2 {
          background-color: #d6d6d6;
          font-size: 1.1em;
          margin:0 0 10px;
          padding:5px;
          left:0;
          width:auto;
        }
        
        div.design-announce {
          border-top:1px solid #33B5E5;
          border-bottom:1px solid #33B5E5;
          padding:5px 10px 10px 55px;
          margin:2em 0;
          background:url('images/icon_design.png') 5px 13px no-repeat;
        }
        
        div.design-announce p {
          margin: .5em 0 0 0;
        }
        
        div.special {
          padding: .5em 1em 1em 1em;
          margin: 0 0 1em;
          background-color: #DAF3FC;
          border:1px solid #d3ecf5;
          border-radius:5px;
          -moz-border-radius:5px;
          -webkit-border-radius:5px;
        }
        
        div.special p {
          margin: .5em 0 0 0;
        }
        
        div.special ol {
          margin: 0;
        }
        
        div.special ol li {
          margin: 0;
          padding: 0;
        }
        
        #jd-content div.special h2,
        #jd-content div.special h3 {
          color:#669999;
          font-size:1.2em;
          border:none;
          margin:0 0 .5em;
          padding:0;
        }
        
        #jd-content div.special.reference h2,
        #jd-content div.special.reference h3,
        #jd-content div.special.reference h4 {
          color:#000;
          font-size:1em;
          border:none;
          font-weight:bold;
          margin:.5em 0;
          padding:0;
        }
        
        p.note, div.note,
        p.caution, div.caution,
        p.warning, div.warning {
          margin: 1em;
          padding: 0 0 0 .5em;
          border-left: 4px solid;
        }
        
        p.special-note,
        div.special-note {
          background-color:#EBF3DB;
          padding:10px 20px;
          margin:0 0 1em;
        }
        
        p.note,
        div.note {
         border-color: #99aacc;
        }
        
        p.warning,
        div.warning {
          border-color: #aa0033;
        }
        
        p.caution,
        div.caution {
          border-color: #ffcf00;
        }
        
        li .note,
        li .caution,
        li .warning {
          margin: .5em 0 0 0;
          padding: .2em .5em .2em .9em;
        }
        
        /* Makes sure the first paragraph does not add top-whitespace within the box*/
        li .note>p:first-child,
        li .caution>p:first-child,
        li .warning>p:first-child {
          margin-top:0;
          padding-top:0;
        }
        
        dl.xml dt {
          font-variant:small-caps;
          font-size:1.2em;
        }
        
        dl.xml dl {
          padding:0;
        }
        
        dl.xml dl dt {
          font-variant:normal;
          font-size:1em;
        }
        
        .listhead li {
          font-weight: bold;
        }
        
        .listhead li *, /*ie*/.listhead li li {
          font-weight: normal;
        }
        
        ol.no-style,
        ul.no-style {
          list-style:none;
          padding-left:1em;
        }
        
        .new,
        .new-child {
          font-size: .78em;
          font-weight: bold;
          color: #ff3d3d;
          text-decoration: none;
          vertical-align:top;
          line-height:.9em;
          white-space:nowrap;
        }
        
        .toggle-list.open .new-child {
          display:none;
        }
        
        pre.classic {
          background-color:transparent;
          border:none;
          padding:0;
        }
        
        p.img-caption {
          margin: -0.5em 0 1em 1em; /* matches default img left-margin */
        }
        
        div.figure {
          float:right;
          clear:right;
          margin:1em 0 0 0;
          padding:0 0 0 3em;
          background-color:#fff;
          /* width must be defined w/ an inline style matching the image width */
        }
        
        #jd-content
        div.figure img {
          margin: 0 0 1em;
        }
        
        div.figure p.img-caption {
          margin: -0.5em 0 1em 0;
        }
        
        p.table-caption {
          margin: 0 0 0.5em 1em; /* matches default table left-margin */
        }
        
        
        /* toggle for misc content (such as long sample code) 
           see toggleContent() script in android-developer-docs.js */
        .toggle-content.closed .toggle-content-toggleme {
          display:none;
        }
        
        .toggle-content a[href="#"] {
          text-decoration:none;
          color:inherit;
        }
        
        .toggle-content-toggleme {
          padding-bottom:1px; /* fixes animation bounce due to margins */
        }
        
        #jd-content .toggle-content img.toggle-content-img {
          margin:0;
        }
        
        
        /* BEGIN quickview sidebar element styles */
        
        #qv-wrapper {
          float: right;
          width:310px; /* +35px padding */
          background-color:#fff;
          margin:-48px 0 2px 0;
          padding:0 0 20px 35px;
        }
        
        #qv {
          background-color:#fff;
          border:4px solid #dee8f1;
          margin:0;
          padding:0 5px 5px;
          width:292px; /* +10px padding; +8px border */
          font-size:.9em;
        }
        
        #qv ol {
          list-style:none;
          padding: 0;
        }
        
        #qv ol ol{
          list-style:none;
          padding: 0 0 0 12px;
          margin:0;
        }
        
        #qv ul {
          padding: 0 10px 0 2em;
        }
        
        #qv li {
          padding: 0 10px 3px;
          line-height: 1.2em;
        }
        
        #qv li li {
          padding: 3px 10px 0;
        }
        
        #qv ul li {
          padding: 0 10px 0 0;
        }
        
        #qv li.selected a {
          color:#555;
          text-decoration:none;
        }
        
        #qv a,
        #qv a code {
          color:#cc6600;
        }
        
        #qv p {
          margin:8px 0 0;
          padding:0 10px;
        }
        
        #jd-content #qv h2 {
          font-size:1.05em;
          font-weight:bold;
          margin:12px 0 .25em 0;
          padding:0 10px;
          background-color:transparent;
          color:#7BB026;
          border:none;
          left:0;
          z-index:1;
        }
        
        #qv-extra #rule {
          padding: 0 10px;
          margin: 0;
        }
        
        #qv-sub-rule {
          padding: 5px 15px 10px;
          margin: 0;
        }
        
        #jd-content
        #qv-sub-rule h2 {
          margin: 0 0 .5em 0;
        }
        
        /* END quickview sidebar element styles */
        
        /* Begin sidebox sidebar element styles */
        
        .sidebox-wrapper {
          float:right;
          clear:right;
          width:310px; /* +35px padding */
          background-color:#fff;
          margin:0;
          padding:0 0 20px 35px;
        }
        
        .sidebox {
          border-left:1px solid #dee8f1;
          background-color:#ffffee;
          margin:0;
          padding:8px 12px;
          font-size:0.9em;
          width:285px; /* +24px padding; +1px border */
        }
        
        .sidebox p {
          margin-bottom: .75em;
        }
        
        .sidebox ul {
          padding: 0 0 0 1.5em;
        }
        
        .sidebox li ul {
          margin-top:0;
          margin-bottom:.1em;
        }
        
        .sidebox li {
        padding:0 0 0 0em;
        }
        
        #jd-content .sidebox h2,
        #jd-content .sidebox h3,
        #jd-content .sidebox h4,
        #jd-content .sidebox h5 {
          border:none;
          font-size:1em;
          margin:0;
          padding:0 0 8px;
          left:0;
          z-index:0;
        }
        
        .sidebox hr {
          background-color:#ccc;
          border:none;
        }
        
        /* End sidebox sidebar element styles */
        
        /* BEGIN developer training bar styles */
        
        div#tb-wrapper {
          float: right;
          clear:right;
          width:380px; /* +25px padding = 405 */
          background-color:#fff;
          margin:0 0 2px 0;
          padding:0 0 20px 25px;
        }
        
        div#tb {
          margin:0;
          padding:0 15px;
          width:350px; /* +15px padding = 380 */
          font-size:.9em;
          background:#e9e9e9;
          border:1px solid #aaa;
          border-radius:5px;
          -moz-border-radius:5px;
          -webkit-border-radius:5px;
          overflow:auto;
        }
        
        div#tb h2 {
          font-size:1.3em;
          font-weight:bold;
          margin:1em 0;
          padding:0;
          background-color:transparent;
          border:none;
          clear:both;
        }
        
        div.download-box a.button {
          color: #069;
          font-size:1.1em;
          font-weight:bold;
          text-decoration:none;
          height:27px;
          line-height:27px;
          text-align:center;
          padding:5px 8px;
          background-color: #fff;
          border: 1px solid #aaa;
          -webkit-border-radius: 2px;
          -moz-border-radius: 2px;
          border-radius: 2px;
        }
        
        div.download-box a.button:hover {
          border-color: #09C;
          background-color: #4CADCB;
          background-image: -webkit-gradient(linear,left top,left bottom,from(#5dbcd9),to(#4cadcb));
          background-image: -webkit-linear-gradient(top,#5dbcd9,#4cadcb);
          background-image: -moz-linear-gradient(top,#5dbcd9,#4cadcb);
          background-image: -ms-linear-gradient(top,#5dbcd9,#4cadcb);
          background-image: -o-linear-gradient(top,#5dbcd9,#4cadcb);
          background-image: linear-gradient(top,#5dbcd9,#4cadcb);
          filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#5dbcd9',EndColorStr='#4cadcb');
          color: #fff;
        }
        
        div.download-box a.button:active {
          background-color: #1E799A;
          background-image: none;
          border-color: #30B7E6;
        }
        
        div.download-box p.filename {
          font-size:0.85em;
          color:#888;
          margin:4px 0 1em 10px;
        }
        
        /* End developer training bar */
        
        /* Training nav bar (previous/next) */
        
        div.training-nav-top {
          float: right;
          width:380px; /* +25px padding = 405 */
          margin:-58px 0 0 0;
          padding:0 0 20px 25px;
        }
        
        div.training-nav-bottom {
          padding:1px; /* for weird FF bug (scrollbar appears) */
          margin:3em 0;
          overflow:auto;
        }
        
        div.training-nav-button-next a,
        div.training-nav-button-previous a {
          display:block;
          width:160px;
          height:55px;
          padding:4px 7px;
          border:1px solid #aaa;
          border-radius:5px;
          -moz-border-radius:5px;
          -webkit-border-radius:5px;
          text-decoration:none;
          font-weight:bold;
        }
        
        div.training-nav-button-next a:hover,
        div.training-nav-button-previous a:hover {
          border:1px solid #069; /* match link color */
        }
        
        div.training-nav-button-next a:active,
        div.training-nav-button-previous a:active {
          border:1px solid #f00; /* match link color */
        }
          
        div.training-nav-button-previous {
          float:left;
          text-align:left;
        }
        
        div.training-nav-button-next {
          float:right;
          text-align:right;
        }
        
        span.training-nav-button-title {
          display:block;
          font-size:.85em;
          font-weight:normal;
          line-height:1.3em;
          margin:.5em 0 0;
        }
        
        /* End training nav bar */
        
        /* BEGIN image and caption styles (originally for UI Guidelines docs) */
        
        table.image-caption {
          padding:0;
          margin:.5em 0;
          border:0;
        }
        
        td.image-caption-i {
          font-size:92%;
          padding:0 5px;
          margin:0;
          border:0;
        }
        
        td.image-caption-i img {
          padding:0 1em;
          margin:0;
        }
        
        .image-list {
          width:24px;
          text-align:center;
        }
        
        td.image-caption-c {
          font-size:92%;
          padding:1em 2px 2px 2px;
          margin:0;
          border:0;
          width:350px;
        }
        
        .grad-rule-top {
        background-image:url(images/grad-rule-qv.png);
        background-repeat:no-repeat;
        padding-top:1em;
        margin-top:0;
        }
        
        .image-caption-nested {
          margin-top:0;
          padding:0 0 0 1em;
        }
        
        .image-caption-nested td {
          padding:0 4px 2px 0;
          margin:0;
          border:0;
        }
        
        /* END image and caption styles */
        
        /* table of contents */
        
        ol.toc {
          margin: 0 0 1em 0;
          padding: 0;
          list-style: none;
          font-size:95%;
        }
        
        ol.toc li {
          font-weight: bold;
          margin: 0 0 .5em 1em;
          padding: 0;
        }
        
        ol.toc li p {
          font-weight: normal;
        }
        
        ol.toc li ol {
          margin: 0;
          padding: 0;
        }
        
        ol.toc li li {
          padding: 0;
          margin: 0 0 0 1em;
          font-weight: normal;
          list-style: none;
        }
        
        table ol.toc {
          margin-left: 0;
        }
        
        .columns td {
          padding:0 5px;
          border:none;
        }
        
        /* link table */
        .jd-linktable {
          margin: 0 0 1em;
          border-bottom: 1px solid #888;
        }
        .jd-linktable th,
        .jd-linktable td {
          padding: 3px 5px;
          vertical-align: top;
          text-align: left;
          border:none;
        }
        .jd-linktable tr {
          background-color: #fff;
        }
        .jd-linktable td {
          border-top: 1px solid #888;
          background-color: inherit;
        }
        .jd-linktable td  p {
          padding: 0 0 5px;
        }
        .jd-linktable .jd-linkcol {
        }
        .jd-linktable .jd-descrcol {
        }
        .jd-linktable .jd-typecol {
          text-align:right;
        }
        .jd-linktable .jd-valcol {
        }
        .jd-linktable .jd-commentrow {
          border-top:none;
          padding-left:25px;
        }
        .jd-deprecated-warning {
          margin-top: 0;
          margin-bottom: 10px;
        }
        
        tr.alt-color {
          background-color: #f6f6f6;
        }
        
        /* expando trigger */
        #jd-content .jd-expando-trigger-img {
          margin:0;
        }
        
        /* jd-expando */
        .jd-inheritedlinks {
          padding:0 0 0 13px
        }
        
        /* SDK PAGE */
        table.download tr {
          background-color:#d9d9d9;
        }
        
        table.download tr.alt-color {
          background-color:#ededed;
        }
        
        table.download td,
        table.download th {
          border:2px solid #fff;
          padding:10px 5px;
        }
        
        table.download th {
          background-color:#6d8293;
          color:#fff;
        }
        
        /* INLAY 180 COPY and 240PX EXTENSION */
        /* modified to 43px so that all browsers eliminate the package panel h-scroll */
        .g-tpl-240 .g-unit,
        .g-unit .g-tpl-240 .g-unit,
        .g-unit .g-unit .g-tpl-240 .g-unit {
          display: block;
          margin: 0 0 0 243px;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-240 .g-first,
        .g-unit .g-tpl-240 .g-first,
        .g-tpl-240 .g-first {
          display: block;
          margin: 0;
          width: 243px;
          float: left;
        }
        /* 240px alt */
        .g-tpl-240-alt .g-unit,
        .g-unit .g-tpl-240-alt .g-unit,
        .g-unit .g-unit .g-tpl-240-alt .g-unit {
          display: block;
          margin: 0 243px 0 0;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-240-alt .g-first,
        .g-unit .g-tpl-240-alt .g-first,
        .g-tpl-240-alt .g-first {
          display: block;
          margin: 0;
          width: 243px;
          float: right;
        }
        
        /* 200px */
        .g-tpl-200 .g-unit,
        .g-unit .g-tpl-200 .g-unit,
        .g-unit .g-unit .g-tpl-200 .g-unit {
          display: block;
          margin: 0 0 0 200px;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-200 .g-first,
        .g-unit .g-tpl-200 .g-first,
        .g-tpl-200 .g-first {
          display: block;
          margin: 0;
          width: 200px;
          float: left;
        }
        /* 200px alt */
        .g-tpl-200-alt .g-unit,
        .g-unit .g-tpl-200-alt .g-unit,
        .g-unit .g-unit .g-tpl-200-alt .g-unit {
          display: block;
          margin: 0 200px 0 0;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-200-alt .g-first,
        .g-unit .g-tpl-200-alt .g-first,
        .g-tpl-200-alt .g-first {
          display: block;
          margin: 0;
          width: 200px;
          float: right;
        }
        
        /* 190px */
        .g-tpl-190 .g-unit,
        .g-unit .g-tpl-190 .g-unit,
        .g-unit .g-unit .g-tpl-190 .g-unit {
          display: block;
          margin: 0 0 0 190px;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-190 .g-first,
        .g-unit .g-tpl-190 .g-first,
        .g-tpl-190 .g-first {
          display: block;
          margin: 0;
          width: 190px;
          float: left;
        }
        /* 190px alt */
        .g-tpl-190-alt .g-unit,
        .g-unit .g-tpl-190-alt .g-unit,
        .g-unit .g-unit .g-tpl-190-alt .g-unit {
          display: block;
          margin: 0 190px 0 0;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-190-alt .g-first,
        .g-unit .g-tpl-190-alt .g-first,
        .g-tpl-190-alt .g-first {
          display: block;
          margin: 0;
          width: 190px;
          float: right;
        }
        
        /* 180px */
        .g-tpl-180 .g-unit,
        .g-unit .g-tpl-180 .g-unit,
        .g-unit .g-unit .g-tpl-180 .g-unit {
          display: block;
          margin: 0 0 0 180px;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-180 .g-first,
        .g-unit .g-tpl-180 .g-first,
        .g-tpl-180 .g-first {
          display: block;
          margin: 0;
          width: 180px;
          float: left;
        }
        /* 180px alt */
        .g-tpl-180-alt .g-unit,
        .g-unit .g-tpl-180-alt .g-unit,
        .g-unit .g-unit .g-tpl-180-alt .g-unit {
          display: block;
          margin: 0 180px 0 0;
          width: auto;
          float: none;
        }
        .g-unit .g-unit .g-tpl-180-alt .g-first,
        .g-unit .g-tpl-180-alt .g-first,
        .g-tpl-180-alt .g-first {
          display: block;
          margin: 0;
          width: 180px;
          float: right;
        }
        
        
        /* JQUERY RESIZABLE STYLES */
        .ui-resizable { position: relative; }
        .ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; z-index:1; }
        .ui-resizable .ui-resizable-handle { display: block; }
        body .ui-resizable-disabled .ui-resizable-handle { display: none; }
        body .ui-resizable-autohide .ui-resizable-handle { display: none; }
        .ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px;
          background: transparent url("images/resizable-s2.gif") repeat scroll center top; }
        .ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%;
          background: transparent url("images/resizable-e2.gif") repeat scroll right center; }
        
        @media print {
        
          body {
            overflow:visible;
          }
        
          #header {
            height:60px;
          }
        
          #headerLeft {
            padding:0;
          }
        
          #header-tabs,
          #headerRight,
          #side-nav,
          #api-info-block {
            display:none;
          }
        
          #body-content {
            position:inherit;
          }
        
          #doc-content {
            margin-left:0 !important;
            height:auto !important;
            width:auto !important;
            overflow:inherit;
            display:inline;
          }
        
          #jd-header {
            padding:10px 0;
          }
        
          #jd-content {
            padding:15px 0 0;
          }
        
          #footer {
            float:none;
            margin:2em 0 0;
          }
        
          h4.jd-details-title {
            border-bottom:1px solid #666;
          }
        
          pre {
            /* these allow lines to break (if there's a white space) */
            overflow: visible;
            text-wrap: unrestricted;
            white-space: -moz-pre-wrap; /* Moz */
            white-space: -pre-wrap; /* Opera 4-6 */
            white-space: -o-pre-wrap; /* Opera 7 */
            white-space: pre-wrap; /* CSS3  */
            word-wrap: break-word; /* IE 5.5+ */
          }
        
          h1, h2, h3, h4, h5, h6 {
            page-break-after: avoid;
          }
        
          table, img {
            page-break-inside: avoid;
          }
        }
        
        
        ================================================
        FILE: tools/droiddoc/templates-pdk/assets/android-developer-docs.js
        ================================================
        var resizePackagesNav;
        var classesNav;
        var devdocNav;
        var sidenav;
        var content;
        var HEADER_HEIGHT = 117;
        var cookie_namespace = 'android_developer';
        var NAV_PREF_TREE = "tree";
        var NAV_PREF_PANELS = "panels";
        var nav_pref;
        var toRoot;
        var isMobile = false; // true if mobile, so we can adjust some layout
        var isIE6 = false; // true if IE6
        
        // TODO: use $(document).ready instead
        function addLoadEvent(newfun) {
          var current = window.onload;
          if (typeof window.onload != 'function') {
            window.onload = newfun;
          } else {
            window.onload = function() {
              current();
              newfun();
            }
          }
        }
        
        var agent = navigator['userAgent'].toLowerCase();
        // If a mobile phone, set flag and do mobile setup
        if ((agent.indexOf("mobile") != -1) ||      // android, iphone, ipod
            (agent.indexOf("blackberry") != -1) ||
            (agent.indexOf("webos") != -1) ||
            (agent.indexOf("mini") != -1)) {        // opera mini browsers
          isMobile = true;
          addLoadEvent(mobileSetup);
        // If not a mobile browser, set the onresize event for IE6, and others
        } else if (agent.indexOf("msie 6") != -1) {
          isIE6 = true;
          addLoadEvent(function() {
            window.onresize = resizeAll;
          });
        } else {
          addLoadEvent(function() {
            window.onresize = resizeHeight;
          });
        }
        
        function mobileSetup() {
          $("body").css({'overflow':'auto'});
          $("html").css({'overflow':'auto'});
          $("#body-content").css({'position':'relative', 'top':'0'});
          $("#doc-content").css({'overflow':'visible', 'border-left':'3px solid #DDD'});
          $("#side-nav").css({'padding':'0'});
          $("#nav-tree").css({'overflow-y': 'auto'});
        }
        
        /* loads the lists.js file to the page.
        Loading this in the head was slowing page load time */
        addLoadEvent( function() {
          var lists = document.createElement("script");
          lists.setAttribute("type","text/javascript");
          lists.setAttribute("src", toRoot+"reference/lists.js");
          document.getElementsByTagName("head")[0].appendChild(lists);
        } );
        
        addLoadEvent( function() {
          $("pre:not(.no-pretty-print)").addClass("prettyprint");
          prettyPrint();
        } );
        
        function setToRoot(root) {
          toRoot = root;
          // note: toRoot also used by carousel.js
        }
        
        function restoreWidth(navWidth) {
          var windowWidth = $(window).width() + "px";
          content.css({marginLeft:parseInt(navWidth) + 6 + "px"}); //account for 6px-wide handle-bar
        
          if (isIE6) {
            content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); // necessary in order for scrollbars to be visible
          }
        
          sidenav.css({width:navWidth});
          resizePackagesNav.css({width:navWidth});
          classesNav.css({width:navWidth});
          $("#packages-nav").css({width:navWidth});
        }
        
        function restoreHeight(packageHeight) {
          var windowHeight = ($(window).height() - HEADER_HEIGHT);
          var swapperHeight = windowHeight - 13;
          $("#swapper").css({height:swapperHeight + "px"});
          sidenav.css({height:windowHeight + "px"});
          content.css({height:windowHeight + "px"});
          resizePackagesNav.css({maxHeight:swapperHeight + "px", height:packageHeight});
          classesNav.css({height:swapperHeight - parseInt(packageHeight) + "px"});
          $("#packages-nav").css({height:parseInt(packageHeight) - 6 + "px"}); //move 6px to give space for the resize handle
          devdocNav.css({height:sidenav.css("height")});
          $("#nav-tree").css({height:swapperHeight + "px"});
        }
        
        function readCookie(cookie) {
          var myCookie = cookie_namespace+"_"+cookie+"=";
          if (document.cookie) {
            var index = document.cookie.indexOf(myCookie);
            if (index != -1) {
              var valStart = index + myCookie.length;
              var valEnd = document.cookie.indexOf(";", valStart);
              if (valEnd == -1) {
                valEnd = document.cookie.length;
              }
              var val = document.cookie.substring(valStart, valEnd);
              return val;
            }
          }
          return 0;
        }
        
        function writeCookie(cookie, val, section, expiration) {
          if (val==undefined) return;
          section = section == null ? "_" : "_"+section+"_";
          if (expiration == null) {
            var date = new Date();
            date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
            expiration = date.toGMTString();
          }
          document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/";
        }
        
        function init() {
          $("#side-nav").css({position:"absolute",left:0});
          content = $("#doc-content");
          resizePackagesNav = $("#resize-packages-nav");
          classesNav = $("#classes-nav");
          sidenav = $("#side-nav");
          devdocNav = $("#devdoc-nav");
        
          var cookiePath = "";
          if (location.href.indexOf("/reference/") != -1) {
            cookiePath = "reference_";
          } else if (location.href.indexOf("/guide/") != -1) {
            cookiePath = "guide_";
          } else if (location.href.indexOf("/sdk/") != -1) {
            cookiePath = "sdk_";
          } else if ((location.href.indexOf("/resources/") != -1) || 
                     (location.href.indexOf("/training/") != -1)) {
            cookiePath = "resources_";
          }
        
          if (!isMobile) {
            $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizePackagesHeight(); } });
            $("#side-nav").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
            var cookieWidth = readCookie(cookiePath+'width');
            var cookieHeight = readCookie(cookiePath+'height');
            if (cookieWidth) {
              restoreWidth(cookieWidth);
            } else if ($("#side-nav").length) {
              resizeWidth();
            }
            if (cookieHeight) {
              restoreHeight(cookieHeight);
            } else {
              resizeHeight();
            }
          }
        
          if (devdocNav.length) { // only dev guide, resources, and sdk
            tryPopulateResourcesNav();
            highlightNav(location.href);
          }
        }
        
        function tryPopulateResourcesNav() {
          var sampleList = $('#devdoc-nav-sample-list');
          var articleList = $('#devdoc-nav-article-list');
          var tutorialList = $('#devdoc-nav-tutorial-list');
          var topicList = $('#devdoc-nav-topic-list');
        
          if (!topicList.length || !ANDROID_TAGS || !ANDROID_RESOURCES)
            return;
        
          var topics = [];
          for (var topic in ANDROID_TAGS['topic']) {
            topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]});
          }
          topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; });
          for (var i = 0; i < topics.length; i++) {
            topicList.append(
                $('
      • ').append( $('') .attr('href', toRoot + "resources/browser.html?tag=" + topics[i].name) .append($('') .addClass('en') .html(topics[i].title) ) ) ); } var _renderResourceList = function(tag, listNode) { var resources = []; var tags; var resource; var i, j; for (i = 0; i < ANDROID_RESOURCES.length; i++) { resource = ANDROID_RESOURCES[i]; tags = resource.tags || []; var hasTag = false; for (j = 0; j < tags.length; j++) if (tags[j] == tag) { hasTag = true; break; } if (!hasTag) continue; resources.push(resource); } //resources.sort(function(x,y){ return (x.title.en < y.title.en) ? -1 : 1; }); for (i = 0; i < resources.length; i++) { resource = resources[i]; var listItemNode = $('
      • ').append( $('') .attr('href', toRoot + "resources/" + resource.path) .append($('') .addClass('en') .html(resource.title.en) ) ); tags = resource.tags || []; for (j = 0; j < tags.length; j++) { if (tags[j] == 'new') { listItemNode.get(0).innerHTML += ' new!'; break; } else if (tags[j] == 'updated') { listItemNode.get(0).innerHTML += ' updated!'; break; } } listNode.append(listItemNode); } }; _renderResourceList('sample', sampleList); _renderResourceList('article', articleList); _renderResourceList('tutorial', tutorialList); } function highlightNav(fullPageName) { var lastSlashPos = fullPageName.lastIndexOf("/"); var firstSlashPos; if (fullPageName.indexOf("/guide/") != -1) { firstSlashPos = fullPageName.indexOf("/guide/"); } else if (fullPageName.indexOf("/sdk/") != -1) { firstSlashPos = fullPageName.indexOf("/sdk/"); } else if (fullPageName.indexOf("/resources/") != -1) { firstSlashPos = fullPageName.indexOf("/resources/"); } else if (fullPageName.indexOf("/training/") != -1) { firstSlashPos = fullPageName.indexOf("/training/"); } if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html') fullPageName = fullPageName + "index.html"; } // get the path and page name from the URL (such as 'guide/topics/graphics/index.html') var htmlPos = fullPageName.indexOf(".html"); var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); // +5 advances past ".html" // find instances of the page name in the side nav var link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); // if there's no match, then let's backstep through the directory until we find an index.html // page that matches our ancestor directories (only for dev guide and resources) if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/resources/") != -1))) { lastBackstep = pathPageName.lastIndexOf("/"); while (link.length == 0) { backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 1)+"index.html']"); lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1); if (lastBackstep == 0) break; } } // add 'selected' to the
      • or
      • ) and the parent
          is hidden else if (link.parent().parent().is(':hidden')) { toggle(link.parent().parent().parent(), false); // open the parent list // then also check if the parent list is also nested in a hidden list if (link.parent().parent().parent().parent().is(':hidden')) { toggle(link.parent().parent().parent().parent().parent(), false); // open the super parent list } } } /* Resize the height of the nav panels in the reference, * and save the new size to a cookie */ function resizePackagesHeight() { var windowHeight = ($(window).height() - HEADER_HEIGHT); var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom resizePackagesNav.css({maxHeight:swapperHeight + "px"}); classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); $("#swapper").css({height:swapperHeight + "px"}); $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle var basePath = getBaseUri(location.pathname); var section = basePath.substring(1,basePath.indexOf("/",1)); writeCookie("height", resizePackagesNav.css("height"), section, null); } /* Resize the height of the side-nav and doc-content divs, * which creates the frame effect */ function resizeHeight() { var docContent = $("#doc-content"); // Get the window height and always resize the doc-content and side-nav divs var windowHeight = ($(window).height() - HEADER_HEIGHT); docContent.css({height:windowHeight + "px"}); $("#side-nav").css({height:windowHeight + "px"}); var href = location.href; // If in the reference docs, also resize the "swapper", "classes-nav", and "nav-tree" divs if (href.indexOf("/reference/") != -1) { var swapperHeight = windowHeight - 13; $("#swapper").css({height:swapperHeight + "px"}); $("#classes-nav").css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); $("#nav-tree").css({height:swapperHeight + "px"}); // Also resize the "devdoc-nav" div } else if ($("#devdoc-nav").length) { $("#devdoc-nav").css({height:sidenav.css("height")}); } // Hide the "Go to top" link if there's no vertical scroll if ( parseInt($("#jd-content").css("height")) <= parseInt(docContent.css("height")) ) { $("a[href='#top']").css({'display':'none'}); } else { $("a[href='#top']").css({'display':'inline'}); } } /* Resize the width of the "side-nav" and the left margin of the "doc-content" div, * which creates the resizable side bar */ function resizeWidth() { var windowWidth = $(window).width() + "px"; var sidenav = $("#side-nav"); if (sidenav.length) { var sidenavWidth = sidenav.css("width"); } else { var sidenavWidth = 0; } content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px"}); //account for 6px-wide handle-bar if (isIE6) { content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); // necessary in order to for scrollbars to be visible } resizePackagesNav.css({width:sidenavWidth}); classesNav.css({width:sidenavWidth}); $("#packages-nav").css({width:sidenavWidth}); if (sidenav.length) { // Must check if the nav exists because IE6 calls resizeWidth() from resizeAll() for all pages var basePath = getBaseUri(location.pathname); var section = basePath.substring(1,basePath.indexOf("/",1)); section = section.indexOf("training") != -1 ? "resources" : section; writeCookie("width", sidenavWidth, section, null); } } /* For IE6 only, * because it can't properly perform auto width for "doc-content" div, * avoiding this for all browsers provides better performance */ function resizeAll() { resizeHeight(); resizeWidth(); } function getBaseUri(uri) { var intlUrl = (uri.substring(0,6) == "/intl/"); if (intlUrl) { base = uri.substring(uri.indexOf('intl/')+5,uri.length); base = base.substring(base.indexOf('/')+1, base.length); //alert("intl, returning base url: /" + base); return ("/" + base); } else { //alert("not intl, returning uri as found."); return uri; } } function requestAppendHL(uri) { //append "?hl= to an outgoing request (such as to blog) var lang = getLangPref(); if (lang) { var q = 'hl=' + lang; uri += '?' + q; window.location = uri; return false; } else { return true; } } function loadLast(cookiePath) { var location = window.location.href; if (location.indexOf("/"+cookiePath+"/") != -1) { return true; } var lastPage = readCookie(cookiePath + "_lastpage"); if (lastPage) { window.location = lastPage; return false; } return true; } $(window).unload(function(){ var path = getBaseUri(location.pathname); if (path.indexOf("/reference/") != -1) { writeCookie("lastpage", path, "reference", null); } else if (path.indexOf("/guide/") != -1) { writeCookie("lastpage", path, "guide", null); } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) { writeCookie("lastpage", path, "resources", null); } }); function toggle(obj, slide) { var ul = $("ul:first", obj); var li = ul.parent(); if (li.hasClass("closed")) { if (slide) { ul.slideDown("fast"); } else { ul.show(); } li.removeClass("closed"); li.addClass("open"); $(".toggle-img", li).attr("title", "hide pages"); } else { ul.slideUp("fast"); li.removeClass("open"); li.addClass("closed"); $(".toggle-img", li).attr("title", "show pages"); } } function buildToggleLists() { $(".toggle-list").each( function(i) { $("div:first", this).append(""); $(this).addClass("closed"); }); } function getNavPref() { var v = readCookie('reference_nav'); if (v != NAV_PREF_TREE) { v = NAV_PREF_PANELS; } return v; } function chooseDefaultNav() { nav_pref = getNavPref(); if (nav_pref == NAV_PREF_TREE) { $("#nav-panels").toggle(); $("#panel-link").toggle(); $("#nav-tree").toggle(); $("#tree-link").toggle(); } } function swapNav() { if (nav_pref == NAV_PREF_TREE) { nav_pref = NAV_PREF_PANELS; } else { nav_pref = NAV_PREF_TREE; init_default_navtree(toRoot); } var date = new Date(); date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years writeCookie("nav", nav_pref, "reference", date.toGMTString()); $("#nav-panels").toggle(); $("#panel-link").toggle(); $("#nav-tree").toggle(); $("#tree-link").toggle(); if ($("#nav-tree").is(':visible')) scrollIntoView("nav-tree"); else { scrollIntoView("packages-nav"); scrollIntoView("classes-nav"); } } function scrollIntoView(nav) { var navObj = $("#"+nav); if (navObj.is(':visible')) { var selected = $(".selected", navObj); if (selected.length == 0) return; if (selected.is("div")) selected = selected.parent(); // when the selected item is a parent var scrolling = document.getElementById(nav); var navHeight = navObj.height(); var offsetTop = selected.position().top; // handle nested items if (selected.parent().parent().is(".toggle-list")) { selected = selected.parent().parent(); // handle second level nested items if (selected.parent().parent().is(".toggle-list")) { selected = selected.parent().parent(); } offsetTop += selected.position().top; } // 180px from the bottom of the list is the threshold if(offsetTop > navHeight - 180) { scrolling.scrollTop = offsetTop - navHeight + 180; } } } function changeTabLang(lang) { var nodes = $("#header-tabs").find("."+lang); for (i=0; i < nodes.length; i++) { // for each node in this language var node = $(nodes[i]); node.siblings().css("display","none"); // hide all siblings if (node.not(":empty").length != 0) { //if this languages node has a translation, show it node.css("display","inline"); } else { //otherwise, show English instead node.css("display","none"); node.siblings().filter(".en").css("display","inline"); } } } function changeNavLang(lang) { var nodes = $("#side-nav").find("."+lang); for (i=0; i < nodes.length; i++) { // for each node in this language var node = $(nodes[i]); node.siblings().css("display","none"); // hide all siblings if (node.not(":empty").length != 0) { // if this languages node has a translation, show it node.css("display","inline"); } else { // otherwise, show English instead node.css("display","none"); node.siblings().filter(".en").css("display","inline"); } } } function changeDocLang(lang) { changeTabLang(lang); changeNavLang(lang); } function changeLangPref(lang, refresh) { var date = new Date(); expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years //alert("expires: " + expires) writeCookie("pref_lang", lang, null, expires); //changeDocLang(lang); if (refresh) { l = getBaseUri(location.pathname); window.location = l; } } function loadLangPref() { var lang = readCookie("pref_lang"); if (lang != 0) { $("#language").find("option[value='"+lang+"']").attr("selected",true); } } function getLangPref() { var lang = $("#language").find(":selected").attr("value"); if (!lang) { lang = readCookie("pref_lang"); } return (lang != 0) ? lang : 'en'; } /* Used to hide and reveal supplemental content, such as long code samples. See the companion CSS in android-developer-docs.css */ function toggleContent(obj) { var div = $(obj.parentNode.parentNode); var toggleMe = $(".toggle-content-toggleme",div); if (div.hasClass("closed")) { // if it's closed, open it toggleMe.slideDown(); $(".toggle-content-text", obj).toggle(); div.removeClass("closed").addClass("open"); $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot + "assets/images/triangle-opened.png"); } else { // if it's open, close it toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow $(".toggle-content-text", obj).toggle(); div.removeClass("open").addClass("closed"); $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot + "assets/images/triangle-closed.png"); }); } return false; } ================================================ FILE: tools/droiddoc/templates-pdk/assets/android-developer-reference.js ================================================ /* API LEVEL TOGGLE */ addLoadEvent(changeApiLevel); var API_LEVEL_ENABLED_COOKIE = "api_level_enabled"; var API_LEVEL_COOKIE = "api_level"; var minLevel = 1; var maxLevel = 1; function toggleApiLevelSelector(checkbox) { var date = new Date(); date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years var expiration = date.toGMTString(); if (checkbox.checked) { $("#apiLevelSelector").removeAttr("disabled"); $("#api-level-toggle label").removeClass("disabled"); writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration); } else { $("#apiLevelSelector").attr("disabled","disabled"); $("#api-level-toggle label").addClass("disabled"); writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration); } changeApiLevel(); } function buildApiLevelSelector() { maxLevel = SINCE_DATA.length; var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE); var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE)); userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default if (userApiLevelEnabled == 0) { $("#apiLevelSelector").attr("disabled","disabled"); } else { $("#apiLevelCheckbox").attr("checked","checked"); $("#api-level-toggle label").removeClass("disabled"); } minLevel = parseInt($("body").attr("class")); // Handle provisional api levels; the provisional level will always be the highest possible level // Provisional api levels will also have a length; other stuff that's just missing a level won't, // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class) if (isNaN(minLevel) && minLevel.length) { minLevel = maxLevel; } var select = $("#apiLevelSelector").html("").change(changeApiLevel); for (var i = maxLevel-1; i >= 0; i--) { var option = $("