[automerger skipped] Merge changes from topic "cherrypicker-L80700000960770298:N69600001370607907" into udc-dev am: f96858a818 am: 89dd3c7e38 am: 90d290f97c -s ours
am skip reason: Merged-In I41622366e5e6ed9dc78cca7bc7bb69a1f8f9bd9f with SHA-1 233d5b97f8 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/build/+/23305031
Change-Id: I72e8ae282c4735d309b0ff6e1e5112e5f37b199d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Changes.md b/Changes.md
index daebd52..6d20173 100644
--- a/Changes.md
+++ b/Changes.md
@@ -1,5 +1,18 @@
# Build System Changes for Android.mk Writers
+## 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 list of
+plugins to omit from the validation.
+
## Python 2 to 3 migration
The path set when running builds now makes the `python` executable point to python 3,
@@ -15,7 +28,6 @@
variable to `true`.
Python 2 is slated for complete removal in V.
-
## Stop referencing sysprop_library directly from cc modules
For the migration to Bazel, we are no longer mapping sysprop_library targets
diff --git a/core/Makefile b/core/Makefile
index 6561420..ccfa3f2 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -17,6 +17,52 @@
SYSTEM_DLKM_NOTICE_DEPS :=
# -----------------------------------------------------------------
+# Release Config Flags
+
+# Create a summary file of build flags for each partition
+# $(1): build flags json file
+# $(2): flag names
+define generate-partition-build-flag-file
+$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
+$(eval $(strip $(1)): PRIVATE_FLAG_NAMES := $(strip $(2)))
+$(strip $(1)):
+ mkdir -p $$(dir $$(PRIVATE_OUT))
+ ( \
+ echo '{' ; \
+ echo 'flags: [' ; \
+ $$(foreach flag, $$(PRIVATE_FLAG_NAMES), \
+ printf ' { "name": "%s", "value": "%s", ' \
+ '$$(flag)' \
+ '$$(_ALL_RELEASE_FLAGS.$$(flag).VALUE)' \
+ ; \
+ printf '"set": "%s", "default": "%s", "declared": "%s", }' \
+ '$$(_ALL_RELEASE_FLAGS.$$(flag).SET_IN)' \
+ '$$(_ALL_RELEASE_FLAGS.$$(flag).DEFAULT)' \
+ '$$(_ALL_RELEASE_FLAGS.$$(flag).DECLARED_IN)' \
+ ; \
+ printf '$$(if $$(filter $$(lastword $$(PRIVATE_FLAG_NAMES)),$$(flag)),,$$(comma))\n' ; \
+ ) \
+ echo "]" ; \
+ echo "}" \
+ ) >> $$(PRIVATE_OUT)
+endef
+
+$(foreach partition, $(_FLAG_PARTITIONS), \
+ $(eval BUILD_FLAG_SUMMARIES.$(partition) \
+ := $(TARGET_OUT_FLAGS)/$(partition)/etc/build_flags.json) \
+ $(eval $(call generate-partition-build-flag-file, \
+ $(BUILD_FLAG_SUMMARIES.$(partition)), \
+ $(_ALL_RELEASE_FLAGS.PARTITIONS.$(partition)) \
+ ) \
+ ) \
+)
+
+# TODO: Remove
+.PHONY: flag-files
+flag-files: $(foreach partition, $(_FLAG_PARTITIONS), \
+ $(TARGET_OUT_FLAGS)/$(partition)/etc/build_flags.json)
+
+# -----------------------------------------------------------------
# Define rules to copy PRODUCT_COPY_FILES defined by the product.
# PRODUCT_COPY_FILES contains words like <source file>:<dest file>[:<owner>].
# <dest file> is relative to $(PRODUCT_OUT), so it should look like,
@@ -530,6 +576,24 @@
$(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.
@@ -595,6 +659,8 @@
$(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)),system,modules.load,,$(kmd))) \
$(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\
@@ -3288,8 +3354,8 @@
endif # BUILDING_SYSTEM_IMAGE
-.PHONY: sync syncsys
-sync syncsys: $(INTERNAL_SYSTEMIMAGE_FILES)
+.PHONY: sync syncsys sync_system
+sync syncsys sync_system: $(INTERNAL_SYSTEMIMAGE_FILES)
# -----------------------------------------------------------------
# Old PDK fusion targets
@@ -3617,7 +3683,8 @@
vendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-vendorimage-target)
-sync: $(INTERNAL_VENDORIMAGE_FILES)
+.PHONY: sync_vendor
+sync sync_vendor: $(INTERNAL_VENDORIMAGE_FILES)
else ifdef BOARD_PREBUILT_VENDORIMAGE
INSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
@@ -3681,7 +3748,8 @@
productimage-nodeps pnod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-productimage-target)
-sync: $(INTERNAL_PRODUCTIMAGE_FILES)
+.PHONY: sync_product
+sync sync_product: $(INTERNAL_PRODUCTIMAGE_FILES)
else ifdef BOARD_PREBUILT_PRODUCTIMAGE
INSTALLED_PRODUCTIMAGE_TARGET := $(PRODUCT_OUT)/product.img
@@ -3743,7 +3811,8 @@
systemextimage-nodeps senod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-system_extimage-target)
-sync: $(INTERNAL_SYSTEM_EXTIMAGE_FILES)
+.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
@@ -3824,7 +3893,8 @@
odmimage-nodeps onod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-odmimage-target)
-sync: $(INTERNAL_ODMIMAGE_FILES)
+.PHONY: sync_odm
+sync sync_odm: $(INTERNAL_ODMIMAGE_FILES)
else ifdef BOARD_PREBUILT_ODMIMAGE
INSTALLED_ODMIMAGE_TARGET := $(PRODUCT_OUT)/odm.img
@@ -3885,7 +3955,8 @@
vendor_dlkmimage-nodeps vdnod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-vendor_dlkmimage-target)
-sync: $(INTERNAL_VENDOR_DLKMIMAGE_FILES)
+.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
@@ -3946,7 +4017,8 @@
odm_dlkmimage-nodeps odnod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-odm_dlkmimage-target)
-sync: $(INTERNAL_ODM_DLKMIMAGE_FILES)
+.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
@@ -4009,7 +4081,8 @@
system_dlkmimage-nodeps sdnod: | $(INTERNAL_USERIMAGES_DEPS)
$(build-system_dlkmimage-target)
-sync: $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)
+.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
@@ -5144,6 +5217,7 @@
lz4 \
make_f2fs \
make_f2fs_casefold \
+ merge_ota \
merge_target_files \
minigzip \
mk_combined_img \
@@ -5272,6 +5346,62 @@
endif # build_otatools_package
# -----------------------------------------------------------------
+# fastboot-info.txt
+FASTBOOT_INFO_VERSION = 1
+
+INSTALLED_FASTBOOT_INFO_TARGET := $(PRODUCT_OUT)/fastboot-info.txt
+
+$(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
+ifeq ($(BOARD_USES_PVMFWIMAGE),true)
+ $(hide) echo "flash pvmfw" >> $@
+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 --apply-vbmeta vbmeta_system" >> $@
+endif
+ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR)))
+ $(hide) echo "flash --apply-vbmeta vbmeta_vendor" >> $@
+endif
+ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)
+ $(hide) echo "flash vendor_boot" >> $@
+endif
+ifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)))
+ $(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \
+ echo "flash --apply-vbmeta vbmeta_$(partition)" >> $@;)
+endif
+endif # BOARD_AVB_ENABLE
+ $(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
+
+# -----------------------------------------------------------------
# misc_info.txt
INSTALLED_MISC_INFO_TARGET := $(PRODUCT_OUT)/misc_info.txt
@@ -5521,6 +5651,13 @@
$(hide) echo "target_flatten_apex=false" >> $@
endif
+$(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
@@ -5540,10 +5677,12 @@
name := $(name)-target_files-$(FILE_NAME_TAG)
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): intermediates := $(intermediates)
-$(BUILT_TARGET_FILES_PACKAGE): \
- zip_root := $(intermediates)/$(name)
+$(BUILT_TARGET_FILES_PACKAGE): zip_root := $(intermediates)/$(name)
+$(BUILT_TARGET_FILES_DIR): zip_root := $(intermediates)/$(name)
+$(BUILT_TARGET_FILES_DIR): intermediates := $(intermediates)
+
# $(1): Directory to copy
# $(2): Location to copy it to
@@ -5563,10 +5702,10 @@
$(call intermediates-dir-for,EXECUTABLES,updater)/updater
endif
-$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
+$(BUILT_TARGET_FILES_DIR): PRIVATE_OTA_TOOLS := $(built_ota_tools)
tool_extension := $(wildcard $(tool_extensions)/releasetools.py)
-$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_TOOL_EXTENSION := $(tool_extension)
+$(BUILT_TARGET_FILES_DIR): PRIVATE_TOOL_EXTENSION := $(tool_extension)
updater_dep :=
ifeq ($(AB_OTA_UPDATER),true)
@@ -5582,23 +5721,23 @@
updater_dep += $(built_ota_tools)
endif
-$(BUILT_TARGET_FILES_PACKAGE): $(updater_dep)
+$(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_PACKAGE): PRIVATE_RECOVERY_OUT := BOOT
+$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := BOOT
else ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true)
-$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := VENDOR_BOOT
+$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := VENDOR_BOOT
else
-$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := RECOVERY
+$(BUILT_TARGET_FILES_DIR): PRIVATE_RECOVERY_OUT := RECOVERY
endif
ifeq ($(AB_OTA_UPDATER),true)
ifdef OSRELEASED_DIRECTORY
- $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id
- $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version
- $(BUILT_TARGET_FILES_PACKAGE): $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version
+ $(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
# Not checking in board_config.mk, since AB_OTA_PARTITIONS may be updated in Android.mk (e.g. to
@@ -5700,34 +5839,36 @@
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))
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_PACKAGE): $(FULL_SYSTEMIMAGE_DEPS)
+ $(BUILT_TARGET_FILES_DIR): $(FULL_SYSTEMIMAGE_DEPS)
else
# releasetools may need the system build.prop even when building a
# system-image-less product.
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BUILD_PROP_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BUILD_PROP_TARGET)
endif
ifdef BUILDING_USERDATA_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_USERDATAIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_USERDATAIMAGE_FILES)
endif
ifdef BUILDING_SYSTEM_OTHER_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEMOTHERIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEMOTHERIMAGE_FILES)
endif
ifdef BUILDING_VENDOR_BOOT_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FILES)
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS)
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)
+ $(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_PACKAGE): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
endif
endif
@@ -5737,11 +5878,11 @@
# 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_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET)
else
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_RECOVERYIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_RECOVERYIMAGE_TARGET)
endif
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RECOVERYIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_RECOVERYIMAGE_FILES)
endif
# Conditionally depend on the image files if the image is being built so the
@@ -5749,68 +5890,68 @@
# if it is coming from a prebuilt.
ifdef BUILDING_VENDOR_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDORIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDORIMAGE_FILES)
else ifdef BOARD_PREBUILT_VENDORIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_VENDORIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDORIMAGE_TARGET)
endif
ifdef BUILDING_PRODUCT_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_PRODUCTIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_PRODUCTIMAGE_FILES)
else ifdef BOARD_PREBUILT_PRODUCTIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_PRODUCTIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_PRODUCTIMAGE_TARGET)
endif
ifdef BUILDING_SYSTEM_EXT_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEM_EXTIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_EXTIMAGE_FILES)
else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
endif
ifneq (,$(BUILDING_BOOT_IMAGE)$(BUILDING_INIT_BOOT_IMAGE))
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RAMDISK_FILES)
+ $(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_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_BOOTIMAGE_TARGET)
endif
ifdef BUILDING_ODM_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_ODMIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODMIMAGE_FILES)
else ifdef BOARD_PREBUILT_ODMIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_ODMIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODMIMAGE_TARGET)
endif
ifdef BUILDING_VENDOR_DLKM_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_DLKMIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_DLKMIMAGE_FILES)
else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)
endif
ifdef BUILDING_ODM_DLKM_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_ODM_DLKMIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODM_DLKMIMAGE_FILES)
else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_ODM_DLKMIMAGE_TARGET)
+ $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODM_DLKMIMAGE_TARGET)
endif
ifdef BUILDING_SYSTEM_DLKM_IMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)
+ $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)
else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE
- $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)
+ $(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_PACKAGE): $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH)
+ $(BUILT_TARGET_FILES_DIR): $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH)
endif
ifdef BOARD_PREBUILT_BOOTLOADER
-$(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTLOADER_MODULE)
+$(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_PACKAGE): \
+$(BUILT_TARGET_FILES_DIR): \
$(INSTALLED_RADIOIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_CACHEIMAGE_TARGET) \
@@ -5838,6 +5979,7 @@
$(LPMAKE) \
$(SELINUX_FC) \
$(INSTALLED_MISC_INFO_TARGET) \
+ $(INSTALLED_FASTBOOT_INFO_TARGET) \
$(APKCERTS_FILE) \
$(SOONG_APEX_KEYS_FILE) \
$(SOONG_ZIP) \
@@ -5847,7 +5989,7 @@
$(BUILT_KERNEL_CONFIGS_FILE) \
$(BUILT_KERNEL_VERSION_FILE) \
| $(ACP)
- @echo "Package target files: $@"
+ @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)))
@@ -6056,6 +6198,9 @@
$(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))
@@ -6250,13 +6395,19 @@
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 >$@.list
- $(hide) find $(zip_root) -path $(zip_root)/META -prune -o -print | sort >>$@.list
+ $(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
.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) \
@@ -6284,6 +6435,7 @@
$(LPMAKE) \
$(SELINUX_FC) \
$(INSTALLED_MISC_INFO_TARGET) \
+ $(INSTALLED_FASTBOOT_INFO_TARGET) \
$(APKCERTS_FILE) \
$(SOONG_APEX_KEYS_FILE) \
$(HOST_OUT_EXECUTABLES)/fs_config \
@@ -6317,11 +6469,10 @@
PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \
$(OTA_FROM_TARGET_FILES) \
--verbose \
- --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
--path $(HOST_OUT) \
$(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
$(2) \
- $(BUILT_TARGET_FILES_PACKAGE) $(1)
+ $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1)
endef
product_name := $(TARGET_PRODUCT)
@@ -6337,7 +6488,7 @@
$(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_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
+$(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))
@@ -6373,7 +6524,7 @@
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_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
+$(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)")
@@ -6696,7 +6847,7 @@
# 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_PACKAGE) $(BUILD_SUPER_IMAGE)
+$(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) $@
diff --git a/core/all_versions.bzl b/core/all_versions.bzl
new file mode 100644
index 0000000..33da673
--- /dev/null
+++ b/core/all_versions.bzl
@@ -0,0 +1,23 @@
+# 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.
+
+_all_versions = ["OPR1", "OPD1", "OPD2", "OPM1", "OPM2", "PPR1", "PPD1", "PPD2", "PPM1", "PPM2", "QPR1"] + [
+ version + subversion
+ for version in ["Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
+ for subversion in ["P1A", "P1B", "P2A", "P2B", "D1A", "D1B", "D2A", "D2B", "Q1A", "Q1B", "Q2A", "Q2B", "Q3A", "Q3B"]
+]
+
+variables_to_export_to_make = {
+ "ALL_VERSIONS": _all_versions,
+}
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index cfeb238..f2ff201 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -101,6 +101,9 @@
endif
$(call soong_config_set,art_module,source_build,$(ART_MODULE_BUILD_FROM_SOURCE))
+ifdef ART_DEBUG_OPT_FLAG
+$(call soong_config_set,art_module,art_debug_opt_flag,$(ART_DEBUG_OPT_FLAG))
+endif
ifdef TARGET_BOARD_AUTO
$(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))
diff --git a/core/base_rules.mk b/core/base_rules.mk
index c453469..65e80fb 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -190,18 +190,6 @@
$(call pretty-error,unusual tags: $(filter-out tests optional samples,$(my_module_tags)))
endif
-# Add implicit tags.
-#
-# If the local directory or one of its parents contains a MODULE_LICENSE_GPL
-# file, tag the module as "gnu". Search for "*_GPL*", "*_LGPL*" and "*_MPL*"
-# so that we can also find files like MODULE_LICENSE_GPL_AND_AFL
-#
-gpl_license_file := $(call find-parent-file,$(LOCAL_PATH),MODULE_LICENSE*_GPL* MODULE_LICENSE*_MPL* MODULE_LICENSE*_LGPL*)
-ifneq ($(gpl_license_file),)
- my_module_tags += gnu
- ALL_GPL_MODULE_LICENSE_FILES += $(gpl_license_file)
-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)")
diff --git a/core/board_config.mk b/core/board_config.mk
index fae7aaa..c8ec5a9 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -174,6 +174,7 @@
_build_broken_var_list := \
+ BUILD_BROKEN_PLUGIN_VALIDATION \
BUILD_BROKEN_CLANG_PROPERTY \
BUILD_BROKEN_CLANG_ASFLAGS \
BUILD_BROKEN_CLANG_CFLAGS \
@@ -256,7 +257,7 @@
endif
$(shell build/soong/scripts/update_out $(OUT_DIR)/rbc/rbc_board_config_results.mk \
- $(OUT_DIR)/rbcrun RBC_OUT="make" $(OUT_DIR)/rbc/boardlauncher.rbc)
+ $(OUT_DIR)/rbcrun --mode=rbc $(OUT_DIR)/rbc/boardlauncher.rbc)
ifneq ($(.SHELLSTATUS),0)
$(error board configuration runner failed: $(.SHELLSTATUS))
endif
diff --git a/core/config.mk b/core/config.mk
index 0c086ee..6ec63eb 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -42,6 +42,7 @@
# 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_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)
@@ -270,7 +271,7 @@
# Ex: $(call add_soong_config_namespace,acme)
define add_soong_config_namespace
-$(eval SOONG_CONFIG_NAMESPACES += $1) \
+$(eval SOONG_CONFIG_NAMESPACES += $(strip $1)) \
$(eval SOONG_CONFIG_$(strip $1) :=)
endef
@@ -280,8 +281,8 @@
# $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) += $2) \
-$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $($v)))
+$(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
@@ -290,7 +291,7 @@
# Ex: $(call add_soong_config_var_value,acme,COOL_FEATURE,true)
define add_soong_config_var_value
-$(eval $2 := $3) \
+$(eval $(strip $2) := $(strip $3)) \
$(call add_soong_config_var,$1,$2)
endef
@@ -298,8 +299,8 @@
#
# 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) $1)) \
-$(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $2))
+$(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
@@ -308,7 +309,7 @@
# 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):=$3)
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(strip $3))
endef
# soong_config_append appends to the value of the variable in the given Soong
@@ -317,7 +318,7 @@
# $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)) $3)
+$(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
@@ -831,13 +832,6 @@
ifneq ($(call numbers_less_than,$(min_systemsdk_version),$(BOARD_SYSTEMSDK_VERSIONS)),)
$(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to BOARD_API_LEVEL, BOARD_SHIPPING_API_LEVEL or PRODUCT_SHIPPING_API_LEVEL ($(min_systemsdk_version)))
endif
- ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),28),)
- ifneq ($(TARGET_IS_64_BIT), true)
- ifneq ($(TARGET_USES_64_BIT_BINDER), true)
- $(error When PRODUCT_SHIPPING_API_LEVEL >= 28, TARGET_USES_64_BIT_BINDER must be true)
- endif
- endif
- endif
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)
diff --git a/core/definitions.mk b/core/definitions.mk
index e4cee7a..7697211 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -75,9 +75,6 @@
# All findbugs xml files
ALL_FINDBUGS_FILES:=
-# GPL module license files
-ALL_GPL_MODULE_LICENSE_FILES:=
-
# Packages with certificate violation
CERTIFICATE_VIOLATION_MODULES :=
@@ -897,7 +894,8 @@
endef
###########################################################
-## Declare license dependencies $(2) for non-module target $(1)
+## Declare license dependencies $(2) with optional colon-separated
+## annotations for non-module target $(1)
###########################################################
define declare-license-deps
$(strip \
@@ -909,7 +907,8 @@
endef
###########################################################
-## Declare license dependencies $(2) for non-module container-type target $(1)
+## 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.
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index d498875..7165bea 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -84,12 +84,13 @@
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.
- my_classes_directory := $(PRODUCT_DEX_PREOPT_PROFILE_DIR)
- LOCAL_DEX_PREOPT_PROFILE := $(my_classes_directory)/$(LOCAL_MODULE).prof
+ 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 :=
+ 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)
@@ -240,7 +241,7 @@
--enforce-uses-libraries-relax,)
my_dexpreopt_config_args := $(patsubst %,--dexpreopt-config %,$(my_dexpreopt_dep_configs))
- my_enforced_uses_libraries := $(intermediates.COMMON)/enforce_uses_libraries.status
+ 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)
@@ -473,7 +474,7 @@
my_dexpreopt_deps += $(my_dexpreopt_images_deps)
my_dexpreopt_deps += $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)
ifeq ($(LOCAL_ENFORCE_USES_LIBRARIES),true)
- my_dexpreopt_deps += $(intermediates.COMMON)/enforce_uses_libraries.status
+ my_dexpreopt_deps += $(intermediates)/enforce_uses_libraries.status
endif
$(my_dexpreopt_zip): PRIVATE_MODULE := $(LOCAL_MODULE)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 7dd9b12..860ce79 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -24,14 +24,31 @@
#$(warning $(call find_and_earlier,A B C,C))
#$(warning $(call find_and_earlier,A B C,D))
-define version-list
-$(1)P1A $(1)P1B $(1)P2A $(1)P2B $(1)D1A $(1)D1B $(1)D2A $(1)D2B $(1)Q1A $(1)Q1B $(1)Q2A $(1)Q2B $(1)Q3A $(1)Q3B
+# Runs the starlark file given in $(1), 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). This behavior can be modified by passing a list of starlark files
+# to exclude from the dependency list as $(2)
+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 $(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
-PREV_VERSIONS := OPR1 OPD1 OPD2 OPM1 OPM2 PPR1 PPD1 PPD2 PPM1 PPM2 QPR1
-ALL_VERSIONS := Q R S T U V W X Y Z
-ALL_VERSIONS := $(PREV_VERSIONS) $(foreach v,$(ALL_VERSIONS),$(call version-list,$(v)))
-PREV_VERSIONS :=
+# ---------------------------------------------------------------
+# Release config
+include $(BUILD_SYSTEM)/release_config.mk
+
+# ---------------------------------------------------------------
+# defines ALL_VERSIONS
+$(call run-starlark,build/make/core/all_versions.bzl)
# Filters ALL_VERSIONS down to the range [$1, $2], and errors if $1 > $2 or $3 is
# not in [$1, $2]
@@ -339,6 +356,7 @@
RBC_PRODUCT_CONFIG \
RBC_BOARD_CONFIG \
SOONG_% \
+ TARGET_RELEASE \
TOPDIR \
TRACE_BEGIN_SOONG \
USER)
@@ -553,6 +571,8 @@
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 \
@@ -566,7 +586,8 @@
TARGET_OUT_ETC \
TARGET_OUT_NOTICE_FILES \
TARGET_OUT_FAKE \
- TARGET_OUT_TESTCASES
+ 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
diff --git a/core/generate_enforce_rro.mk b/core/generate_enforce_rro.mk
index ed258cc..b002469 100644
--- a/core/generate_enforce_rro.mk
+++ b/core/generate_enforce_rro.mk
@@ -1,6 +1,6 @@
include $(CLEAR_VARS)
-enforce_rro_module := $(enforce_rro_source_module)__auto_generated_rro_$(enforce_rro_partition)
+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)
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 3e9106b..7cfab5b 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -203,10 +203,10 @@
all_resources := $(strip $(my_res_resources) $(my_overlay_resources))
# The linked resource package.
-my_res_package := $(intermediates)/package-res.apk
+my_res_package := $(intermediates.COMMON)/package-res.apk
LOCAL_INTERMEDIATE_TARGETS += $(my_res_package)
-my_bundle_module := $(intermediates)/base.zip
+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.
@@ -572,7 +572,7 @@
$(compress-package)
endif # LOCAL_COMPRESSED_MODULE
-my_package_res_pb := $(intermediates)/package-res.pb.apk
+my_package_res_pb := $(intermediates.COMMON)/package-res.pb.apk
$(my_package_res_pb): $(my_res_package) $(AAPT2)
$(AAPT2) convert --output-format proto $< -o $@
diff --git a/core/product.mk b/core/product.mk
index e90e27b..818aac2 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -404,6 +404,10 @@
# supports it
_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
+
_product_list_vars += PRODUCT_AFDO_PROFILES
.KATI_READONLY := _product_single_value_vars _product_list_vars
diff --git a/core/product_config.mk b/core/product_config.mk
index 1ef8890..01ad030 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -223,7 +223,7 @@
endif
ifeq (,$(current_product_makefile))
- $(error Can not locate config makefile for product "$(TARGET_PRODUCT)")
+ $(error Cannot locate config makefile for product "$(TARGET_PRODUCT)")
endif
ifneq (,$(filter $(TARGET_PRODUCT),$(products_using_starlark_config)))
@@ -236,14 +236,22 @@
$(shell mkdir -p $(OUT_DIR)/rbc)
$(call dump-variables-rbc, $(OUT_DIR)/rbc/make_vars_pre_product_config.mk)
- $(shell build/soong/scripts/update_out \
- $(OUT_DIR)/rbc/rbc_product_config_results.mk \
- build/soong/scripts/rbc-run \
- $(current_product_makefile) \
- $(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
@@ -517,7 +525,8 @@
PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := $(OVERRIDE_PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE)
endif
else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),)
- # No shipping level defined
+ # 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
@@ -532,7 +541,8 @@
ifneq ($(PRODUCT_USE_PRODUCT_VNDK_OVERRIDE),)
PRODUCT_USE_PRODUCT_VNDK := $(PRODUCT_USE_PRODUCT_VNDK_OVERRIDE)
else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),)
- # No shipping level defined
+ # No shipping level defined. Enforce the product interface by default.
+ PRODUCT_USE_PRODUCT_VNDK := true
else ifeq ($(call math_gt,$(PRODUCT_SHIPPING_API_LEVEL),29),true)
# Enforce product interface for VNDK if PRODUCT_SHIPPING_API_LEVEL is greater
# than 29.
diff --git a/core/product_config.rbc b/core/product_config.rbc
index 97c1d00..921f068 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -54,25 +54,16 @@
if value == None:
return
if type(value) == "list":
- if _options.rearrange:
- value = __printvars_rearrange_list(value)
- if _options.format == "pretty":
- print(attr, "=", repr(value))
- elif _options.format == "make":
- 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))
- elif _options.format == "pretty":
- print(attr, "=", repr(value))
- elif _options.format == "make":
+ 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))
- else:
- fail("bad output format", _options.format)
def _printvars(state):
"""Prints configuration and global variables."""
@@ -83,8 +74,7 @@
for nsname, nsvars in sorted(val.items()):
# Define SOONG_CONFIG_<ns> for Make, othewise
# it cannot be added to .KATI_READONLY list
- if _options.format == "make":
- print("SOONG_CONFIG_" + nsname, ":=", " ".join(nsvars.keys()))
+ 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)
@@ -105,11 +95,6 @@
elif attr not in globals_base or globals_base[attr] != val:
__print_attr(attr, val)
-def __printvars_rearrange_list(value_list):
- """Rearrange value list: return only distinct elements, maybe sorted."""
- seen = {item: 0 for item in value_list}
- return sorted(seen.keys()) if _options.rearrange == "sort" else seen.keys()
-
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.
@@ -394,7 +379,7 @@
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]=value
+ g[_soong_config_namespaces_key][nsname][var]=_mkstrip(value)
def _soong_config_append(g, nsname, var, value):
"""Appends to the value of the variable in the namespace."""
@@ -402,9 +387,9 @@
ns = g[_soong_config_namespaces_key][nsname]
oldv = ns.get(var)
if oldv == None:
- ns[var] = value
+ ns[var] = _mkstrip(value)
else:
- ns[var] += " " + value
+ ns[var] += " " + _mkstrip(value)
def _soong_config_get(g, nsname, var):
@@ -691,16 +676,8 @@
rblf_log(file, "warning", message, sep = ':')
def _mk2rbc_error(loc, message):
- """Prints a message about conversion error and stops.
-
- If RBC_MK2RBC_CONTINUE environment variable is set,
- the execution will continue after the message is printed.
- """
- if _options.mk2rbc_continue:
- rblf_log(loc, message, sep = ':')
- else:
- _mkerror(loc, message)
-
+ """Prints a message about conversion error and stops."""
+ _mkerror(loc, message)
def _mkinfo(file, message = ""):
"""Prints info."""
@@ -873,39 +850,12 @@
# Cause the variable to appear set like the make version does
g[v] = ""
-
-def __get_options():
- """Returns struct containing runtime global settings."""
- settings = dict(
- format = "pretty",
- rearrange = "",
- trace_modules = False,
- trace_variables = [],
- mk2rbc_continue = False,
- )
- for x in getattr(rblf_cli, "RBC_OUT", "").split(","):
- if x == "sort" or x == "unique":
- if settings["rearrange"]:
- fail("RBC_OUT: either sort or unique is allowed (and sort implies unique)")
- settings["rearrange"] = x
- elif x == "pretty" or x == "make":
- settings["format"] = x
- elif x == "global":
- # TODO: Remove this, kept for backwards compatibility
- pass
- elif x != "":
- fail("RBC_OUT: got %s, should be one of: [pretty|make] [sort|unique]" % x)
- for x in getattr(rblf_cli, "RBC_DEBUG", "").split(","):
- if x == "!trace":
- settings["trace_modules"] = True
- elif x != "":
- settings["trace_variables"].append(x)
- if getattr(rblf_cli, "RBC_MK2RBC_CONTINUE", ""):
- settings["mk2rbc_continue"] = True
- return struct(**settings)
-
# Settings used during debugging.
-_options = __get_options()
+_options = struct(
+ trace_modules = False,
+ trace_variables = [],
+)
+
rblf = struct(
soong_config_namespace = _soong_config_namespace,
soong_config_append = _soong_config_append,
diff --git a/core/release_config.bzl b/core/release_config.bzl
new file mode 100644
index 0000000..e73c90f
--- /dev/null
+++ b/core/release_config.bzl
@@ -0,0 +1,90 @@
+# 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.
+
+# Partitions that get build system flag summaries
+_flag_partitions = [
+ "product",
+ "system",
+ "system_ext",
+ "vendor",
+]
+
+def _combine_dicts_no_duplicate_keys(dicts):
+ result = {}
+ for d in dicts:
+ for k, v in d.items():
+ if k in result:
+ fail("Duplicate key: " + k)
+ result[k] = v
+ return result
+
+def release_config(target_release, flag_definitions, config_maps, fail_if_no_release_config = True):
+ result = {
+ "_ALL_RELEASE_FLAGS": [flag.name for flag in flag_definitions],
+ }
+ all_flags = {}
+ for flag in flag_definitions:
+ if sorted(dir(flag)) != ["default", "name", "partitions"]:
+ fail("Flag structs must contain 3 fields: name, partitions, and default")
+ if not flag.partitions:
+ fail("At least 1 partition is required")
+ 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))
+ elif partition not in _flag_partitions:
+ fail("Invalid partition: " + flag.partition + ", allowed partitions: " + str(_flag_partitions))
+ if not flag.name.startswith("RELEASE_"):
+ fail("Release flag names must start with RELEASE_")
+ if " " in flag.name or "\t" in flag.name or "\n" in flag.name:
+ fail("Flag names must not contain whitespace.")
+ if flag.name in all_flags:
+ fail("Duplicate declaration of flag " + flag.name)
+ all_flags[flag.name] = True
+
+ default = flag.default
+ if type(default) == "bool":
+ default = "true" if default else ""
+
+ result["_ALL_RELEASE_FLAGS." + flag.name + ".PARTITIONS"] = flag.partitions
+ result["_ALL_RELEASE_FLAGS." + flag.name + ".DEFAULT"] = default
+ result["_ALL_RELEASE_FLAGS." + flag.name + ".VALUE"] = default
+
+ # If TARGET_RELEASE is set, fail if there is no matching release config
+ # If it isn't set, no release config files will be included and all flags
+ # will get their default values.
+ if target_release:
+ config_map = _combine_dicts_no_duplicate_keys(config_maps)
+ if target_release not in config_map:
+ fail("No release config found for TARGET_RELEASE: " + target_release + ". Available releases are: " + str(config_map.keys()))
+ release_config = config_map[target_release]
+ if sorted(dir(release_config)) != ["flags", "release_version"]:
+ fail("A release config must be a struct with a flags and release_version fields")
+ result["_RELEASE_VERSION"] = release_config.release_version
+ for flag in release_config.flags:
+ if sorted(dir(release_config)) != ["name", "value"]:
+ fail("A flag be a struct with name and value fields")
+ if flag.name not in all_flags:
+ fail("Undeclared build flag: " + flag.name)
+ value = flag.value
+ if type(value) == "bool":
+ value = "true" if value else ""
+ result["_ALL_RELEASE_FLAGS." + flag.name + ".VALUE"] = value
+ elif fail_if_no_release_config:
+ fail("FAIL_IF_NO_RELEASE_CONFIG was set and TARGET_RELEASE was not")
+ else:
+ # No TARGET_RELEASE means release version 0
+ result["_RELEASE_VERSION"] = 0
+
+ return result
diff --git a/core/release_config.mk b/core/release_config.mk
new file mode 100644
index 0000000..621c7d1
--- /dev/null
+++ b/core/release_config.mk
@@ -0,0 +1,69 @@
+# 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.
+
+# 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.
+# TODO: Remove wildcard for build/release one when all branch manifests
+# have updated.
+flag_declaration_files := $(wildcard build/release/build_flags.bzl) \
+ $(if $(wildcard vendor/google/release/build_flags.bzl), \
+ vendor/google/release/build_flags.bzl, \
+ $(sort \
+ $(wildcard device/*/release/build_flags.bzl) \
+ $(wildcard device/*/*/release/build_flags.bzl) \
+ $(wildcard vendor/*/release/build_flags.bzl) \
+ $(wildcard vendor/*/*/release/build_flags.bzl) \
+ ) \
+ )
+config_map_files := $(wildcard build/release/release_config_map.bzl) \
+ $(if $(wildcard vendor/google/release/release_config_map.bzl), \
+ vendor/google/release/release_config_map.bzl, \
+ $(sort \
+ $(wildcard device/*/release/release_config_map.bzl) \
+ $(wildcard device/*/*/release/release_config_map.bzl) \
+ $(wildcard vendor/*/release/release_config_map.bzl) \
+ $(wildcard vendor/*/*/release/release_config_map.bzl) \
+ ) \
+ )
+
+# 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.bzl", "release_config")
+_c+=$(foreach f,$(flag_declaration_files),$(newline)load("//$(f)", flags_$(call filename_to_starlark,$(f)) = "flags"))
+_c+=$(foreach f,$(config_map_files),$(newline)load("//$(f)", config_maps_$(call filename_to_starlark,$(f)) = "config_maps"))
+_c+=$(newline)all_flags = [] $(foreach f,$(flag_declaration_files),+ flags_$(call filename_to_starlark,$(f)))
+_c+=$(newline)all_config_maps = [$(foreach f,$(config_map_files),config_maps_$(call filename_to_starlark,$(f))$(comma))]
+_c+=$(newline)target_release = "$(TARGET_RELEASE)"
+_c+=$(newline)fail_if_no_release_config = True if "$(FAIL_IF_NO_RELEASE_CONFIG)" else False
+_c+=$(newline)variables_to_export_to_make = release_config(target_release, all_flags, all_config_maps, fail_if_no_release_config)
+$(file >$(OUT_DIR)/release_config_entrypoint.bzl,$(_c))
+_c:=
+filename_to_starlark:=
+
+# 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
+
+# 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.
+$(call run-starlark,$(OUT_DIR)/release_config_entrypoint.bzl,$(OUT_DIR)/release_config_entrypoint.bzl)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index a149e2a..7dad167 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -2,13 +2,6 @@
SOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.variables
SOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT).mk
-BINDER32BIT :=
-ifneq ($(TARGET_USES_64_BIT_BINDER),true)
-ifneq ($(TARGET_IS_64_BIT),true)
-BINDER32BIT := true
-endif
-endif
-
include $(BUILD_SYSTEM)/art_config.mk
include $(BUILD_SYSTEM)/dex_preopt_config.mk
@@ -143,7 +136,6 @@
$(call add_json_bool, SamplingPGO, $(filter true,$(SAMPLING_PGO)))
$(call add_json_bool, ArtUseReadBarrier, $(call invert_bool,$(filter false,$(PRODUCT_ART_USE_READ_BARRIER))))
-$(call add_json_bool, Binder32bit, $(BINDER32BIT))
$(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, DeviceVndkVersion, $(BOARD_VNDK_VERSION))
@@ -293,6 +285,7 @@
$(call add_json_str, ShippingApiLevel, $(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)))
@@ -327,6 +320,9 @@
$(call add_json_str, ProductBrand, $(PRODUCT_BRAND))
$(call add_json_list, BuildVersionTags, $(BUILD_VERSION_TAGS))
+$(call add_json_str, ReleaseVersion, $(_RELEASE_VERSION))
+$(call add_json_list, ReleaseDeviceConfigValueSets, $(RELEASE_DEVICE_CONFIG_VALUE_SETS))
+
$(call json_end)
$(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/tasks/collect_gpl_sources.mk b/core/tasks/collect_gpl_sources.mk
deleted file mode 100644
index 9e9ab8e..0000000
--- a/core/tasks/collect_gpl_sources.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-
-# The rule below doesn't have dependenices on the files that it copies,
-# so manually generate into a PACKAGING intermediate dir, which is wiped
-# in installclean between incremental builds on build servers.
-gpl_source_tgz := $(call intermediates-dir-for,PACKAGING,gpl_source)/gpl_source.tgz
-
-ALL_GPL_MODULE_LICENSE_FILES := $(sort $(ALL_GPL_MODULE_LICENSE_FILES))
-
-# FORCE since we can't know whether any of the sources changed
-$(gpl_source_tgz): PRIVATE_PATHS := $(sort $(patsubst %/, %, $(dir $(ALL_GPL_MODULE_LICENSE_FILES))))
-$(gpl_source_tgz) : $(ALL_GPL_MODULE_LICENSE_FILES)
- @echo Package GPL sources: $@
- $(hide) tar cfz $@ --exclude ".git*" $(PRIVATE_PATHS)
-
-# Dist the tgz only if we are doing a full build
-$(call dist-for-goals,droidcore-unbundled,$(gpl_source_tgz))
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index b42476d..8ae2a9a 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -30,7 +30,6 @@
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)/tradefed-test-framework.jar \
$(HOST_OUT_JAVA_LIBRARIES)/loganalysis.jar \
$(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \
$(HOST_OUT_JAVA_LIBRARIES)/compatibility-tradefed.jar \
@@ -45,10 +44,16 @@
# 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): $(shell find $(test_suite_jdk_dir) -type f | sort)
+$(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
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 09a0598..4d1e3df 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -40,7 +40,7 @@
include $(INTERNAL_BUILD_ID_MAKEFILE)
endif
-DEFAULT_PLATFORM_VERSION := UP1A
+DEFAULT_PLATFORM_VERSION := VP1A
.KATI_READONLY := DEFAULT_PLATFORM_VERSION
MIN_PLATFORM_VERSION := UP1A
MAX_PLATFORM_VERSION := VP1A
@@ -53,7 +53,7 @@
# These are the current development codenames, if the build is not a final
# release build. If this is a final release build, it is simply "REL".
-PLATFORM_VERSION_CODENAME.UP1A := REL
+PLATFORM_VERSION_CODENAME.UP1A := UpsideDownCake
PLATFORM_VERSION_CODENAME.VP1A := VanillaIceCream
# This is the user-visible version. In a final release build it should
@@ -91,7 +91,7 @@
Base Base11 Cupcake Donut Eclair Eclair01 EclairMr1 Froyo Gingerbread GingerbreadMr1 \
Honeycomb HoneycombMr1 HoneycombMr2 IceCreamSandwich IceCreamSandwichMr1 \
JellyBean JellyBeanMr1 JellyBeanMr2 Kitkat KitkatWatch Lollipop LollipopMr1 M N NMr1 O OMr1 P \
-Q R S Sv2 Tiramisu UpsideDownCake
+Q R S Sv2 Tiramisu UpsideDownCake VanillaIceCream
# Convert from space separated list to comma separated
PLATFORM_VERSION_KNOWN_CODENAMES := \
diff --git a/envsetup.sh b/envsetup.sh
index 905635c..d292dbb 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -312,7 +312,7 @@
# would prevent exporting type info from those packages.
#
# http://b/266688086
- export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages:
+ 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
@@ -804,13 +804,19 @@
export TARGET_BUILD_APPS=
- local product variant_and_version variant version
+ # Support either <product>-<variant> or <product>-<release>-<variant>
+ local product release_and_variant release variant
product=${selection%%-*} # Trim everything after first dash
- variant_and_version=${selection#*-} # Trim everything up to first dash
- if [ "$variant_and_version" != "$selection" ]; then
- variant=${variant_and_version%%-*}
- if [ "$variant" != "$variant_and_version" ]; then
- version=${variant_and_version#*-}
+ release_and_variant=${selection#*-} # Trim everything up to first dash
+ if [ "$release_and_variant" != "$selection" ]; then
+ local first=${release_and_variant%%-*} # Trim everything after first dash
+ if [ "$first" != "$release_and_variant" ]; then
+ # There is a 2nd dash, split into release-variant
+ release=$first # Everything up to the dash
+ variant=${release_and_variant#*-} # Trim everything up to dash
+ else
+ # There is not a 2nd dash, default to variant as the second param
+ variant=$first
fi
fi
@@ -823,7 +829,7 @@
TARGET_PRODUCT=$product \
TARGET_BUILD_VARIANT=$variant \
- TARGET_PLATFORM_VERSION=$version \
+ TARGET_RELEASE=$release \
build_build_var_cache
if [ $? -ne 0 ]
then
@@ -835,10 +841,10 @@
fi
export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
- if [ -n "$version" ]; then
- export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
+ if [ -n "$release" ]; then
+ export TARGET_RELEASE=$release
else
- unset TARGET_PLATFORM_VERSION
+ unset TARGET_RELEASE
fi
export TARGET_BUILD_TYPE=release
@@ -1096,12 +1102,12 @@
#
# Easy way to make system.img/etc writable
function syswrite() {
- adb wait-for-device && adb root || return 1
+ adb wait-for-device && adb root && adb wait-for-device || return 1
if [[ $(adb disable-verity | grep -i "reboot") ]]; then
echo "rebooting"
- adb reboot && adb wait-for-device && adb root || return 1
+ adb reboot && adb wait-for-device && adb root && adb wait-for-device || return 1
fi
- adb wait-for-device && adb remount || return 1
+ adb remount || return 1
}
# coredump_setup - enable core dumps globally for any process
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index 4d95b33..67e31df 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -36,6 +36,7 @@
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
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index 00f6e5b..01ebe56 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -14,6 +14,8 @@
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
@@ -22,9 +24,6 @@
# Default is current, but allow devices to override vndk version if needed.
BOARD_VNDK_VERSION ?= current
-# Required flag for non-64 bit devices from P.
-TARGET_USES_64_BIT_BINDER := true
-
# 64 bit mediadrmserver
TARGET_ENABLE_MEDIADRM_64 := true
diff --git a/target/board/BoardConfigModuleCommon.mk b/target/board/BoardConfigModuleCommon.mk
deleted file mode 100644
index 24c01a5..0000000
--- a/target/board/BoardConfigModuleCommon.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-# BoardConfigModuleCommon.mk
-#
-# Common compile-time settings for module builds.
-
-# Required for all module devices.
-TARGET_USES_64_BIT_BINDER := true
diff --git a/target/board/mainline_sdk/BoardConfig.mk b/target/board/mainline_sdk/BoardConfig.mk
index f5c2dc6..84f8b2d 100644
--- a/target/board/mainline_sdk/BoardConfig.mk
+++ b/target/board/mainline_sdk/BoardConfig.mk
@@ -18,6 +18,3 @@
HOST_CROSS_OS := linux_bionic
HOST_CROSS_ARCH := x86_64
HOST_CROSS_2ND_ARCH :=
-
-# Required flag for non-64 bit devices from P.
-TARGET_USES_64_BIT_BINDER := true
diff --git a/target/board/module_arm/BoardConfig.mk b/target/board/module_arm/BoardConfig.mk
index 3f35c06..565efc8 100644
--- a/target/board/module_arm/BoardConfig.mk
+++ b/target/board/module_arm/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_ARCH := arm
TARGET_ARCH_VARIANT := armv7-a-neon
TARGET_CPU_VARIANT := generic
diff --git a/target/board/module_arm64/BoardConfig.mk b/target/board/module_arm64/BoardConfig.mk
index 3700056..66e3792 100644
--- a/target/board/module_arm64/BoardConfig.mk
+++ b/target/board/module_arm64/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_VARIANT := generic
diff --git a/target/board/module_arm64only/BoardConfig.mk b/target/board/module_arm64only/BoardConfig.mk
index 3cabf05..6c26579 100644
--- a/target/board/module_arm64only/BoardConfig.mk
+++ b/target/board/module_arm64only/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_VARIANT := generic
diff --git a/target/board/module_x86/BoardConfig.mk b/target/board/module_x86/BoardConfig.mk
index a93ac97..af3fffd 100644
--- a/target/board/module_x86/BoardConfig.mk
+++ b/target/board/module_x86/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_CPU_ABI := x86
TARGET_ARCH := x86
TARGET_ARCH_VARIANT := x86
diff --git a/target/board/module_x86_64/BoardConfig.mk b/target/board/module_x86_64/BoardConfig.mk
index 1ed3be0..1ada027 100644
--- a/target/board/module_x86_64/BoardConfig.mk
+++ b/target/board/module_x86_64/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_CPU_ABI := x86_64
TARGET_ARCH := x86_64
TARGET_ARCH_VARIANT := x86_64
diff --git a/target/board/module_x86_64only/BoardConfig.mk b/target/board/module_x86_64only/BoardConfig.mk
index b0676cb..5b86f0a 100644
--- a/target/board/module_x86_64only/BoardConfig.mk
+++ b/target/board/module_x86_64only/BoardConfig.mk
@@ -13,8 +13,6 @@
# limitations under the License.
#
-include build/make/target/board/BoardConfigModuleCommon.mk
-
TARGET_CPU_ABI := x86_64
TARGET_ARCH := x86_64
TARGET_ARCH_VARIANT := x86_64
diff --git a/target/board/ndk/BoardConfig.mk b/target/board/ndk/BoardConfig.mk
index da8b5f3..b485f8b 100644
--- a/target/board/ndk/BoardConfig.mk
+++ b/target/board/ndk/BoardConfig.mk
@@ -14,7 +14,6 @@
#
TARGET_ARCH_SUITE := ndk
-TARGET_USES_64_BIT_BINDER := true
MALLOC_SVELTE := true
diff --git a/target/product/virtual_ab_ota/android_t_baseline.mk b/target/product/virtual_ab_ota/android_t_baseline.mk
index 418aaa4..f862485 100644
--- a/target/product/virtual_ab_ota/android_t_baseline.mk
+++ b/target/product/virtual_ab_ota/android_t_baseline.mk
@@ -20,3 +20,5 @@
#
# All U+ launching devices should instead use vabc_features.mk.
$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk)
+
+PRODUCT_VIRTUAL_AB_COW_VERSION := 2
diff --git a/tests/product.rbc b/tests/product.rbc
index 9ae6393..b4c6d45 100644
--- a/tests/product.rbc
+++ b/tests/product.rbc
@@ -54,6 +54,7 @@
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:")
diff --git a/tests/run.rbc b/tests/run.rbc
index 33583eb..85d6c09 100644
--- a/tests/run.rbc
+++ b/tests/run.rbc
@@ -144,7 +144,8 @@
"v2": "def"
},
"NS2": {
- "v3": "xyz"
+ "v3": "xyz",
+ "v4": "xyz"
}
},
{k:v for k, v in sorted(ns.items()) }
diff --git a/tools/aconfig/.gitignore b/tools/aconfig/.gitignore
new file mode 100644
index 0000000..1b72444
--- /dev/null
+++ b/tools/aconfig/.gitignore
@@ -0,0 +1,2 @@
+/Cargo.lock
+/target
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
new file mode 100644
index 0000000..9617e0e
--- /dev/null
+++ b/tools/aconfig/Android.bp
@@ -0,0 +1,38 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_protobuf_host {
+ name: "libaconfig_protos",
+ protos: ["protos/aconfig.proto"],
+ crate_name: "aconfig_protos",
+ source_stem: "aconfig_protos",
+ use_protobuf3: true,
+}
+
+rust_defaults {
+ name: "aconfig.defaults",
+ edition: "2021",
+ clippy_lints: "android",
+ lints: "android",
+ srcs: ["src/main.rs"],
+ rustlibs: [
+ "libaconfig_protos",
+ "libanyhow",
+ "libclap",
+ "libprotobuf",
+ "libserde",
+ "libserde_json",
+ "libtinytemplate",
+ ],
+}
+
+rust_binary_host {
+ name: "aconfig",
+ defaults: ["aconfig.defaults"],
+}
+
+rust_test_host {
+ name: "aconfig.test",
+ defaults: ["aconfig.defaults"],
+}
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
new file mode 100644
index 0000000..8517dd2
--- /dev/null
+++ b/tools/aconfig/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "aconfig"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+clap = { version = "4.1.8", features = ["derive"] }
+protobuf = "3.2.0"
+serde = { version = "1.0.152", features = ["derive"] }
+serde_json = "1.0.93"
+tinytemplate = "1.2.1"
+
+[build-dependencies]
+protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/MODULE_LICENSE_APACHE2 b/tools/aconfig/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/aconfig/MODULE_LICENSE_APACHE2
diff --git a/tools/aconfig/OWNERS b/tools/aconfig/OWNERS
new file mode 100644
index 0000000..4e05b00
--- /dev/null
+++ b/tools/aconfig/OWNERS
@@ -0,0 +1,5 @@
+amhk@google.com
+jham@google.com
+joeo@google.com
+opg@google.com
+zhidou@google.com
diff --git a/tools/aconfig/PREUPLOAD.cfg b/tools/aconfig/PREUPLOAD.cfg
new file mode 100644
index 0000000..75ed57c
--- /dev/null
+++ b/tools/aconfig/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Builtin Hooks]
+rustfmt = true
+
+[Builtin Hooks Options]
+rustfmt = --config-path=rustfmt.toml
diff --git a/tools/aconfig/build.rs b/tools/aconfig/build.rs
new file mode 100644
index 0000000..5ef5b60
--- /dev/null
+++ b/tools/aconfig/build.rs
@@ -0,0 +1,17 @@
+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();
+}
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
new file mode 100644
index 0000000..9d36a9e
--- /dev/null
+++ b/tools/aconfig/protos/aconfig.proto
@@ -0,0 +1,78 @@
+// 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;
+
+// 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 {
+ required string name = 1;
+ required string description = 2;
+};
+
+message flag_declarations {
+ required string namespace = 1;
+ repeated flag_declaration flag = 2;
+};
+
+message flag_value {
+ required string namespace = 1;
+ required string name = 2;
+ required flag_state state = 3;
+ required 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
+ required string source = 1;
+ required flag_state state = 2;
+ required flag_permission permission = 3;
+}
+
+message parsed_flag {
+ required string namespace = 1;
+ required string name = 2;
+ required string description = 3;
+ required flag_state state = 4;
+ required flag_permission permission = 5;
+ repeated tracepoint trace = 6;
+}
+
+message parsed_flags {
+ repeated parsed_flag parsed_flag = 1;
+}
diff --git a/tools/aconfig/rustfmt.toml b/tools/aconfig/rustfmt.toml
new file mode 120000
index 0000000..291e99b
--- /dev/null
+++ b/tools/aconfig/rustfmt.toml
@@ -0,0 +1 @@
+../../../soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
new file mode 100644
index 0000000..b9fa324
--- /dev/null
+++ b/tools/aconfig/src/aconfig.rs
@@ -0,0 +1,289 @@
+/*
+ * 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::{anyhow, bail, Context, Error, Result};
+use protobuf::{Enum, EnumOrUnknown};
+use serde::{Deserialize, Serialize};
+
+use crate::cache::{Cache, Item, Tracepoint};
+use crate::protos::{
+ ProtoFlagDeclaration, ProtoFlagDeclarations, ProtoFlagPermission, ProtoFlagState,
+ ProtoFlagValue, ProtoFlagValues, ProtoParsedFlag, ProtoParsedFlags, ProtoTracepoint,
+};
+
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
+pub enum FlagState {
+ Enabled,
+ Disabled,
+}
+
+impl TryFrom<EnumOrUnknown<ProtoFlagState>> for FlagState {
+ type Error = Error;
+
+ fn try_from(proto: EnumOrUnknown<ProtoFlagState>) -> Result<Self, Self::Error> {
+ match ProtoFlagState::from_i32(proto.value()) {
+ Some(ProtoFlagState::ENABLED) => Ok(FlagState::Enabled),
+ Some(ProtoFlagState::DISABLED) => Ok(FlagState::Disabled),
+ None => Err(anyhow!("unknown flag state enum value {}", proto.value())),
+ }
+ }
+}
+
+impl From<FlagState> for ProtoFlagState {
+ fn from(state: FlagState) -> Self {
+ match state {
+ FlagState::Enabled => ProtoFlagState::ENABLED,
+ FlagState::Disabled => ProtoFlagState::DISABLED,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
+pub enum Permission {
+ ReadOnly,
+ ReadWrite,
+}
+
+impl TryFrom<EnumOrUnknown<ProtoFlagPermission>> for Permission {
+ type Error = Error;
+
+ fn try_from(proto: EnumOrUnknown<ProtoFlagPermission>) -> Result<Self, Self::Error> {
+ match ProtoFlagPermission::from_i32(proto.value()) {
+ Some(ProtoFlagPermission::READ_ONLY) => Ok(Permission::ReadOnly),
+ Some(ProtoFlagPermission::READ_WRITE) => Ok(Permission::ReadWrite),
+ None => Err(anyhow!("unknown permission enum value {}", proto.value())),
+ }
+ }
+}
+
+impl From<Permission> for ProtoFlagPermission {
+ fn from(permission: Permission) -> Self {
+ match permission {
+ Permission::ReadOnly => ProtoFlagPermission::READ_ONLY,
+ Permission::ReadWrite => ProtoFlagPermission::READ_WRITE,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct FlagDeclaration {
+ pub name: String,
+ pub description: String,
+}
+
+impl FlagDeclaration {
+ #[allow(dead_code)] // only used in unit tests
+ pub fn try_from_text_proto(text_proto: &str) -> Result<FlagDeclaration> {
+ let proto: ProtoFlagDeclaration = crate::protos::try_from_text_proto(text_proto)
+ .with_context(|| text_proto.to_owned())?;
+ proto.try_into()
+ }
+}
+
+impl TryFrom<ProtoFlagDeclaration> for FlagDeclaration {
+ type Error = Error;
+
+ fn try_from(proto: ProtoFlagDeclaration) -> Result<Self, Self::Error> {
+ let Some(name) = proto.name else {
+ bail!("missing 'name' field");
+ };
+ let Some(description) = proto.description else {
+ bail!("missing 'description' field");
+ };
+ Ok(FlagDeclaration { name, description })
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct FlagDeclarations {
+ pub namespace: String,
+ pub flags: Vec<FlagDeclaration>,
+}
+
+impl FlagDeclarations {
+ pub fn try_from_text_proto(text_proto: &str) -> Result<FlagDeclarations> {
+ let proto: ProtoFlagDeclarations = crate::protos::try_from_text_proto(text_proto)
+ .with_context(|| text_proto.to_owned())?;
+ let Some(namespace) = proto.namespace else {
+ bail!("missing 'namespace' field");
+ };
+ let mut flags = vec![];
+ for proto_flag in proto.flag.into_iter() {
+ flags.push(proto_flag.try_into()?);
+ }
+ Ok(FlagDeclarations { namespace, flags })
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct FlagValue {
+ pub namespace: String,
+ pub name: String,
+ pub state: FlagState,
+ pub permission: Permission,
+}
+
+impl FlagValue {
+ #[allow(dead_code)] // only used in unit tests
+ pub fn try_from_text_proto(text_proto: &str) -> Result<FlagValue> {
+ let proto: ProtoFlagValue = crate::protos::try_from_text_proto(text_proto)?;
+ proto.try_into()
+ }
+
+ pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<FlagValue>> {
+ let proto: ProtoFlagValues = crate::protos::try_from_text_proto(text_proto)?;
+ proto.flag_value.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
+ }
+}
+
+impl TryFrom<ProtoFlagValue> for FlagValue {
+ type Error = Error;
+
+ fn try_from(proto: ProtoFlagValue) -> Result<Self, Self::Error> {
+ let Some(namespace) = proto.namespace else {
+ bail!("missing 'namespace' field");
+ };
+ let Some(name) = proto.name else {
+ bail!("missing 'name' field");
+ };
+ let Some(proto_state) = proto.state else {
+ bail!("missing 'state' field");
+ };
+ let state = proto_state.try_into()?;
+ let Some(proto_permission) = proto.permission else {
+ bail!("missing 'permission' field");
+ };
+ let permission = proto_permission.try_into()?;
+ Ok(FlagValue { namespace, name, state, permission })
+ }
+}
+
+impl From<Cache> for ProtoParsedFlags {
+ fn from(cache: Cache) -> Self {
+ let mut proto = ProtoParsedFlags::new();
+ for item in cache.into_iter() {
+ proto.parsed_flag.push(item.into());
+ }
+ proto
+ }
+}
+
+impl From<Item> for ProtoParsedFlag {
+ fn from(item: Item) -> Self {
+ let mut proto = crate::protos::ProtoParsedFlag::new();
+ proto.set_namespace(item.namespace.to_owned());
+ proto.set_name(item.name.clone());
+ proto.set_description(item.description.clone());
+ proto.set_state(item.state.into());
+ proto.set_permission(item.permission.into());
+ for trace in item.trace.into_iter() {
+ proto.trace.push(trace.into());
+ }
+ proto
+ }
+}
+
+impl From<Tracepoint> for ProtoTracepoint {
+ fn from(tracepoint: Tracepoint) -> Self {
+ let mut proto = ProtoTracepoint::new();
+ proto.set_source(format!("{}", tracepoint.source));
+ proto.set_state(tracepoint.state.into());
+ proto.set_permission(tracepoint.permission.into());
+ proto
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_flag_try_from_text_proto() {
+ let expected = FlagDeclaration {
+ name: "1234".to_owned(),
+ description: "Description of the flag".to_owned(),
+ };
+
+ let s = r#"
+ name: "1234"
+ description: "Description of the flag"
+ "#;
+ let actual = FlagDeclaration::try_from_text_proto(s).unwrap();
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn test_flag_try_from_text_proto_bad_input() {
+ let s = r#"
+ name: "a"
+ "#;
+ let error = FlagDeclaration::try_from_text_proto(s).unwrap_err();
+ assert!(format!("{:?}", error).contains("Message not initialized"));
+
+ let s = r#"
+ description: "Description of the flag"
+ "#;
+ let error = FlagDeclaration::try_from_text_proto(s).unwrap_err();
+ assert!(format!("{:?}", error).contains("Message not initialized"));
+ }
+
+ #[test]
+ fn test_namespace_try_from_text_proto() {
+ let expected = FlagDeclarations {
+ namespace: "ns".to_owned(),
+ flags: vec![
+ FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
+ FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
+ ],
+ };
+
+ let s = r#"
+ namespace: "ns"
+ flag {
+ name: "a"
+ description: "A"
+ }
+ flag {
+ name: "b"
+ description: "B"
+ }
+ "#;
+ let actual = FlagDeclarations::try_from_text_proto(s).unwrap();
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn test_flag_declaration_try_from_text_proto_list() {
+ let expected = FlagValue {
+ namespace: "ns".to_owned(),
+ name: "1234".to_owned(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ };
+
+ let s = r#"
+ namespace: "ns"
+ name: "1234"
+ state: ENABLED
+ permission: READ_ONLY
+ "#;
+ let actual = FlagValue::try_from_text_proto(s).unwrap();
+
+ assert_eq!(expected, actual);
+ }
+}
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
new file mode 100644
index 0000000..c546f7b
--- /dev/null
+++ b/tools/aconfig/src/cache.rs
@@ -0,0 +1,296 @@
+/*
+ * 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, Result};
+use serde::{Deserialize, Serialize};
+use std::io::{Read, Write};
+
+use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
+use crate::commands::Source;
+
+const DEFAULT_FLAG_STATE: FlagState = FlagState::Disabled;
+const DEFAULT_FLAG_PERMISSION: Permission = Permission::ReadWrite;
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Tracepoint {
+ pub source: Source,
+ pub state: FlagState,
+ pub permission: Permission,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Item {
+ // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
+ // closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
+ // struct and use a newtype instead once aconfig has matured. Until then, namespace should
+ // really be a Cow<String>.
+ pub namespace: String,
+ pub name: String,
+ pub description: String,
+ pub state: FlagState,
+ pub permission: Permission,
+ pub trace: Vec<Tracepoint>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Cache {
+ namespace: String,
+ items: Vec<Item>,
+}
+
+impl Cache {
+ pub fn new(namespace: String) -> Result<Cache> {
+ ensure!(!namespace.is_empty(), "empty namespace");
+ Ok(Cache { namespace, items: vec![] })
+ }
+
+ pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
+ serde_json::from_reader(reader).map_err(|e| e.into())
+ }
+
+ pub fn write_to_writer(&self, writer: impl Write) -> Result<()> {
+ serde_json::to_writer(writer, self).map_err(|e| e.into())
+ }
+
+ pub fn add_flag_declaration(
+ &mut self,
+ source: Source,
+ declaration: FlagDeclaration,
+ ) -> Result<()> {
+ ensure!(!declaration.name.is_empty(), "empty flag name");
+ ensure!(!declaration.description.is_empty(), "empty flag description");
+ ensure!(
+ self.items.iter().all(|item| item.name != declaration.name),
+ "failed to declare flag {} from {}: flag already declared",
+ declaration.name,
+ source
+ );
+ self.items.push(Item {
+ namespace: self.namespace.clone(),
+ name: declaration.name.clone(),
+ description: declaration.description,
+ state: DEFAULT_FLAG_STATE,
+ permission: DEFAULT_FLAG_PERMISSION,
+ trace: vec![Tracepoint {
+ source,
+ state: DEFAULT_FLAG_STATE,
+ permission: DEFAULT_FLAG_PERMISSION,
+ }],
+ });
+ Ok(())
+ }
+
+ pub fn add_flag_value(&mut self, source: Source, value: FlagValue) -> Result<()> {
+ ensure!(!value.namespace.is_empty(), "empty flag namespace");
+ ensure!(!value.name.is_empty(), "empty flag name");
+ ensure!(
+ value.namespace == self.namespace,
+ "failed to set values for flag {}/{} from {}: expected namespace {}",
+ value.namespace,
+ value.name,
+ source,
+ self.namespace
+ );
+ let Some(existing_item) = self.items.iter_mut().find(|item| item.name == value.name) else {
+ bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
+ };
+ existing_item.state = value.state;
+ existing_item.permission = value.permission;
+ existing_item.trace.push(Tracepoint {
+ source,
+ state: value.state,
+ permission: value.permission,
+ });
+ Ok(())
+ }
+
+ pub fn iter(&self) -> impl Iterator<Item = &Item> {
+ self.items.iter()
+ }
+
+ pub fn into_iter(self) -> impl Iterator<Item = Item> {
+ self.items.into_iter()
+ }
+
+ pub fn namespace(&self) -> &str {
+ debug_assert!(!self.namespace.is_empty());
+ &self.namespace
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::aconfig::{FlagState, Permission};
+
+ #[test]
+ fn test_add_flag_declaration() {
+ let mut cache = Cache::new("ns".to_string()).unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("first.txt".to_string()),
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ )
+ .unwrap();
+ let error = cache
+ .add_flag_declaration(
+ Source::File("second.txt".to_string()),
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ )
+ .unwrap_err();
+ assert_eq!(
+ &format!("{:?}", error),
+ "failed to declare flag foo from second.txt: flag already declared"
+ );
+ }
+
+ #[test]
+ fn test_add_flag_value() {
+ fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
+ let item = cache.iter().find(|&item| item.name == name).unwrap();
+ item.state == expected.0 && item.permission == expected.1
+ }
+
+ let mut cache = Cache::new("ns".to_string()).unwrap();
+ let error = cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap_err();
+ assert_eq!(
+ &format!("{:?}", error),
+ "failed to set values for flag ns/foo from <memory>: flag not declared"
+ );
+
+ cache
+ .add_flag_declaration(
+ Source::File("first.txt".to_string()),
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ )
+ .unwrap();
+ assert!(check(&cache, "foo", (DEFAULT_FLAG_STATE, DEFAULT_FLAG_PERMISSION)));
+
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Disabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadOnly)));
+
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadWrite,
+ },
+ )
+ .unwrap();
+ assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
+
+ // different namespace -> no-op
+ let error = cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "some-other-namespace".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "failed to set values for flag some-other-namespace/foo from <memory>: expected namespace ns");
+ assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
+ }
+
+ #[test]
+ fn test_reject_empty_cache_namespace() {
+ Cache::new("".to_string()).unwrap_err();
+ }
+
+ #[test]
+ fn test_reject_empty_flag_declaration_fields() {
+ let mut cache = Cache::new("ns".to_string()).unwrap();
+
+ let error = cache
+ .add_flag_declaration(
+ Source::Memory,
+ FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
+ )
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "empty flag name");
+
+ let error = cache
+ .add_flag_declaration(
+ Source::Memory,
+ FlagDeclaration { name: "foo".to_string(), description: "".to_string() },
+ )
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "empty flag description");
+ }
+
+ #[test]
+ fn test_reject_empty_flag_value_files() {
+ let mut cache = Cache::new("ns".to_string()).unwrap();
+ cache
+ .add_flag_declaration(
+ Source::Memory,
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ )
+ .unwrap();
+
+ let error = cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "empty flag namespace");
+
+ let error = cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: "ns".to_string(),
+ name: "".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "empty flag name");
+ }
+}
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
new file mode 100644
index 0000000..cb266f1
--- /dev/null
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -0,0 +1,216 @@
+/*
+ * 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 crate::aconfig::{FlagState, Permission};
+use crate::cache::{Cache, Item};
+use crate::commands::OutputFile;
+
+pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
+ let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+ let readwrite = class_elements.iter().any(|item| item.readwrite);
+ let namespace = cache.namespace().to_lowercase();
+ let context = Context { namespace: namespace.clone(), readwrite, class_elements };
+ let mut template = TinyTemplate::new();
+ template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
+ let contents = template.render("cpp_code_gen", &context)?;
+ let path = ["aconfig", &(namespace + ".h")].iter().collect();
+ Ok(OutputFile { contents: contents.into(), path })
+}
+
+#[derive(Serialize)]
+struct Context {
+ pub namespace: String,
+ pub readwrite: bool,
+ pub class_elements: Vec<ClassElement>,
+}
+
+#[derive(Serialize)]
+struct ClassElement {
+ pub readwrite: bool,
+ pub default_value: String,
+ pub flag_name: String,
+}
+
+fn create_class_element(item: &Item) -> ClassElement {
+ ClassElement {
+ readwrite: item.permission == Permission::ReadWrite,
+ default_value: if item.state == FlagState::Enabled {
+ "true".to_string()
+ } else {
+ "false".to_string()
+ },
+ flag_name: item.name.clone(),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
+ use crate::commands::Source;
+
+ #[test]
+ fn test_cpp_codegen_build_time_flag_only() {
+ let namespace = "my_namespace";
+ let mut cache = Cache::new(namespace.to_string()).unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("aconfig_one.txt".to_string()),
+ FlagDeclaration {
+ name: "my_flag_one".to_string(),
+ description: "buildtime disable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: namespace.to_string(),
+ name: "my_flag_one".to_string(),
+ state: FlagState::Disabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("aconfig_two.txt".to_string()),
+ FlagDeclaration {
+ name: "my_flag_two".to_string(),
+ description: "buildtime enable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: namespace.to_string(),
+ name: "my_flag_two".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ let expect_content = r#"#ifndef my_namespace_HEADER_H
+ #define my_namespace_HEADER_H
+ #include "my_namespace.h"
+
+ namespace my_namespace {
+
+ class my_flag_one {
+ public:
+ virtual const bool value() {
+ return false;
+ }
+ }
+
+ class my_flag_two {
+ public:
+ virtual const bool value() {
+ return true;
+ }
+ }
+
+ }
+ #endif
+ "#;
+ let file = generate_cpp_code(&cache).unwrap();
+ assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+ assert_eq!(
+ expect_content.replace(' ', ""),
+ String::from_utf8(file.contents).unwrap().replace(' ', "")
+ );
+ }
+
+ #[test]
+ fn test_cpp_codegen_runtime_flag() {
+ let namespace = "my_namespace";
+ let mut cache = Cache::new(namespace.to_string()).unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("aconfig_one.txt".to_string()),
+ FlagDeclaration {
+ name: "my_flag_one".to_string(),
+ description: "buildtime disable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("aconfig_two.txt".to_string()),
+ FlagDeclaration {
+ name: "my_flag_two".to_string(),
+ description: "runtime enable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: namespace.to_string(),
+ name: "my_flag_two".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadWrite,
+ },
+ )
+ .unwrap();
+ let expect_content = r#"#ifndef my_namespace_HEADER_H
+ #define my_namespace_HEADER_H
+ #include "my_namespace.h"
+
+ #include <server_configurable_flags/get_flags.h>
+ using namespace server_configurable_flags;
+
+ namespace my_namespace {
+
+ class my_flag_one {
+ public:
+ virtual const bool value() {
+ return GetServerConfigurableFlag(
+ "my_namespace",
+ "my_flag_one",
+ "false") == "true";
+ }
+ }
+
+ class my_flag_two {
+ public:
+ virtual const bool value() {
+ return GetServerConfigurableFlag(
+ "my_namespace",
+ "my_flag_two",
+ "true") == "true";
+ }
+ }
+
+ }
+ #endif
+ "#;
+ let file = generate_cpp_code(&cache).unwrap();
+ assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+ assert_eq!(
+ expect_content.replace(' ', ""),
+ String::from_utf8(file.contents).unwrap().replace(' ', "")
+ );
+ }
+}
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
new file mode 100644
index 0000000..476a89d
--- /dev/null
+++ b/tools/aconfig/src/codegen_java.rs
@@ -0,0 +1,136 @@
+/*
+ * 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::path::PathBuf;
+use tinytemplate::TinyTemplate;
+
+use crate::aconfig::{FlagState, Permission};
+use crate::cache::{Cache, Item};
+use crate::commands::OutputFile;
+
+pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
+ let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+ let readwrite = class_elements.iter().any(|item| item.readwrite);
+ let namespace = cache.namespace();
+ let context = Context { namespace: namespace.to_string(), readwrite, class_elements };
+ let mut template = TinyTemplate::new();
+ template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
+ let contents = template.render("java_code_gen", &context)?;
+ let mut path: PathBuf = namespace.split('.').collect();
+ // TODO: Allow customization of the java class name
+ path.push("Flags.java");
+ Ok(OutputFile { contents: contents.into(), path })
+}
+
+#[derive(Serialize)]
+struct Context {
+ pub namespace: String,
+ pub readwrite: bool,
+ pub class_elements: Vec<ClassElement>,
+}
+
+#[derive(Serialize)]
+struct ClassElement {
+ pub method_name: String,
+ pub readwrite: bool,
+ pub default_value: String,
+ pub feature_name: String,
+ pub flag_name: String,
+}
+
+fn create_class_element(item: &Item) -> ClassElement {
+ ClassElement {
+ method_name: item.name.clone(),
+ readwrite: item.permission == Permission::ReadWrite,
+ default_value: if item.state == FlagState::Enabled {
+ "true".to_string()
+ } else {
+ "false".to_string()
+ },
+ feature_name: item.name.clone(),
+ flag_name: item.name.clone(),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::aconfig::{FlagDeclaration, FlagValue};
+ use crate::commands::Source;
+
+ #[test]
+ fn test_generate_java_code() {
+ let namespace = "com.example";
+ let mut cache = Cache::new(namespace.to_string()).unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("test.txt".to_string()),
+ FlagDeclaration {
+ name: "test".to_string(),
+ description: "buildtime enable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_declaration(
+ Source::File("test2.txt".to_string()),
+ FlagDeclaration {
+ name: "test2".to_string(),
+ description: "runtime disable".to_string(),
+ },
+ )
+ .unwrap();
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: namespace.to_string(),
+ name: "test".to_string(),
+ state: FlagState::Disabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ let expect_content = r#"package com.example;
+
+ import android.provider.DeviceConfig;
+
+ public final class Flags {
+
+ public static boolean test() {
+ return false;
+ }
+
+ public static boolean test2() {
+ return DeviceConfig.getBoolean(
+ "com.example",
+ "test2__test2",
+ false
+ );
+ }
+
+ }
+ "#;
+ let file = generate_java_code(&cache).unwrap();
+ assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
+ assert_eq!(
+ expect_content.replace(' ', ""),
+ String::from_utf8(file.contents).unwrap().replace(' ', "")
+ );
+ }
+}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
new file mode 100644
index 0000000..0bdb0b5
--- /dev/null
+++ b/tools/aconfig/src/commands.rs
@@ -0,0 +1,208 @@
+/*
+ * 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, Context, Result};
+use clap::ValueEnum;
+use protobuf::Message;
+use serde::{Deserialize, Serialize};
+use std::fmt;
+use std::io::Read;
+use std::path::PathBuf;
+
+use crate::aconfig::{FlagDeclarations, FlagValue};
+use crate::cache::Cache;
+use crate::codegen_cpp::generate_cpp_code;
+use crate::codegen_java::generate_java_code;
+use crate::protos::ProtoParsedFlags;
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub enum Source {
+ #[allow(dead_code)] // only used in unit tests
+ Memory,
+ File(String),
+}
+
+impl fmt::Display for Source {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Memory => write!(f, "<memory>"),
+ Self::File(path) => write!(f, "{}", path),
+ }
+ }
+}
+
+pub struct Input {
+ pub source: Source,
+ pub reader: Box<dyn Read>,
+}
+
+pub struct OutputFile {
+ pub path: PathBuf, // relative to some root directory only main knows about
+ pub contents: Vec<u8>,
+}
+
+pub fn create_cache(
+ namespace: &str,
+ declarations: Vec<Input>,
+ values: Vec<Input>,
+) -> Result<Cache> {
+ let mut cache = Cache::new(namespace.to_owned())?;
+
+ for mut input in declarations {
+ let mut contents = String::new();
+ input.reader.read_to_string(&mut contents)?;
+ let dec_list = FlagDeclarations::try_from_text_proto(&contents)
+ .with_context(|| format!("Failed to parse {}", input.source))?;
+ ensure!(
+ namespace == dec_list.namespace,
+ "Failed to parse {}: expected namespace {}, got {}",
+ input.source,
+ namespace,
+ dec_list.namespace
+ );
+ for d in dec_list.flags.into_iter() {
+ cache.add_flag_declaration(input.source.clone(), d)?;
+ }
+ }
+
+ for mut input in values {
+ let mut contents = String::new();
+ input.reader.read_to_string(&mut contents)?;
+ let values_list = FlagValue::try_from_text_proto_list(&contents)
+ .with_context(|| format!("Failed to parse {}", input.source))?;
+ for v in values_list {
+ // TODO: warn about flag values that do not take effect?
+ let _ = cache.add_flag_value(input.source.clone(), v);
+ }
+ }
+
+ Ok(cache)
+}
+
+pub fn create_java_lib(cache: &Cache) -> Result<OutputFile> {
+ generate_java_code(cache)
+}
+
+pub fn create_cpp_lib(cache: &Cache) -> Result<OutputFile> {
+ generate_cpp_code(cache)
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
+pub enum DumpFormat {
+ Text,
+ Debug,
+ Protobuf,
+}
+
+pub fn dump_cache(cache: Cache, format: DumpFormat) -> Result<Vec<u8>> {
+ match format {
+ DumpFormat::Text => {
+ let mut lines = vec![];
+ for item in cache.iter() {
+ lines.push(format!("{}: {:?}\n", item.name, item.state));
+ }
+ Ok(lines.concat().into())
+ }
+ DumpFormat::Debug => {
+ let mut lines = vec![];
+ for item in cache.iter() {
+ lines.push(format!("{:?}\n", item));
+ }
+ Ok(lines.concat().into())
+ }
+ DumpFormat::Protobuf => {
+ let parsed_flags: ProtoParsedFlags = cache.into();
+ let mut output = vec![];
+ parsed_flags.write_to_vec(&mut output)?;
+ Ok(output)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::aconfig::{FlagState, Permission};
+
+ fn create_test_cache() -> Cache {
+ let s = r#"
+ namespace: "ns"
+ flag {
+ name: "a"
+ description: "Description of a"
+ }
+ flag {
+ name: "b"
+ description: "Description of b"
+ }
+ "#;
+ let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
+ let o = r#"
+ flag_value {
+ namespace: "ns"
+ name: "a"
+ state: DISABLED
+ permission: READ_ONLY
+ }
+ "#;
+ let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
+ create_cache("ns", declarations, values).unwrap()
+ }
+
+ #[test]
+ fn test_create_cache() {
+ let cache = create_test_cache(); // calls create_cache
+ let item = cache.iter().find(|&item| item.name == "a").unwrap();
+ assert_eq!(FlagState::Disabled, item.state);
+ assert_eq!(Permission::ReadOnly, item.permission);
+ }
+
+ #[test]
+ fn test_dump_text_format() {
+ let cache = create_test_cache();
+ let bytes = dump_cache(cache, DumpFormat::Text).unwrap();
+ let text = std::str::from_utf8(&bytes).unwrap();
+ assert!(text.contains("a: Disabled"));
+ }
+
+ #[test]
+ fn test_dump_protobuf_format() {
+ use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
+ use protobuf::Message;
+
+ let cache = create_test_cache();
+ let bytes = dump_cache(cache, DumpFormat::Protobuf).unwrap();
+ let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
+
+ assert_eq!(
+ vec!["a".to_string(), "b".to_string()],
+ actual.parsed_flag.iter().map(|item| item.name.clone().unwrap()).collect::<Vec<_>>()
+ );
+
+ let item =
+ actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
+ assert_eq!(item.namespace(), "ns");
+ assert_eq!(item.name(), "b");
+ assert_eq!(item.description(), "Description of b");
+ assert_eq!(item.state(), ProtoFlagState::DISABLED);
+ assert_eq!(item.permission(), ProtoFlagPermission::READ_WRITE);
+ let mut tp = ProtoTracepoint::new();
+ tp.set_source("<memory>".to_string());
+ tp.set_state(ProtoFlagState::DISABLED);
+ tp.set_permission(ProtoFlagPermission::READ_WRITE);
+ assert_eq!(item.trace, vec![tp]);
+ }
+}
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
new file mode 100644
index 0000000..6db5948
--- /dev/null
+++ b/tools/aconfig/src/main.rs
@@ -0,0 +1,149 @@
+/*
+ * 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 anyhow::{anyhow, ensure, 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 aconfig;
+mod cache;
+mod codegen_cpp;
+mod codegen_java;
+mod commands;
+mod protos;
+
+use crate::cache::Cache;
+use commands::{DumpFormat, Input, OutputFile, Source};
+
+fn cli() -> Command {
+ Command::new("aconfig")
+ .subcommand_required(true)
+ .subcommand(
+ Command::new("create-cache")
+ .arg(Arg::new("namespace").long("namespace").required(true))
+ .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append))
+ .arg(Arg::new("values").long("values").action(ArgAction::Append))
+ .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)),
+ )
+ .subcommand(
+ Command::new("create-cpp-lib")
+ .arg(Arg::new("cache").long("cache").required(true))
+ .arg(Arg::new("out").long("out").required(true)),
+ )
+ .subcommand(
+ Command::new("dump")
+ .arg(Arg::new("cache").long("cache").required(true))
+ .arg(
+ Arg::new("format")
+ .long("format")
+ .value_parser(EnumValueParser::<commands::DumpFormat>::new())
+ .default_value("text"),
+ )
+ .arg(Arg::new("out").long("out").default_value("-")),
+ )
+}
+
+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::<T>(arg_name)
+ .ok_or(anyhow!("internal error: required argument '{}' not found", arg_name))
+}
+
+fn open_zero_or_more_files(matches: &ArgMatches, arg_name: &str) -> Result<Vec<Input>> {
+ let mut opened_files = vec![];
+ for path in matches.get_many::<String>(arg_name).unwrap_or_default() {
+ let file = Box::new(fs::File::open(path)?);
+ opened_files.push(Input { source: Source::File(path.to_string()), reader: file });
+ }
+ Ok(opened_files)
+}
+
+fn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> {
+ ensure!(
+ root.is_dir(),
+ "output directory {} does not exist or is not a directory",
+ root.display()
+ );
+ let path = root.join(output_file.path.clone());
+ let parent = path
+ .parent()
+ .ok_or(anyhow!("unable to locate parent of output file {}", path.display()))?;
+ fs::create_dir_all(parent)?;
+ let mut file = fs::File::create(path)?;
+ file.write_all(&output_file.contents)?;
+ Ok(())
+}
+
+fn main() -> Result<()> {
+ let matches = cli().get_matches();
+ match matches.subcommand() {
+ Some(("create-cache", sub_matches)) => {
+ let namespace = get_required_arg::<String>(sub_matches, "namespace")?;
+ let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
+ let values = open_zero_or_more_files(sub_matches, "values")?;
+ let cache = commands::create_cache(namespace, declarations, values)?;
+ let path = get_required_arg::<String>(sub_matches, "cache")?;
+ let file = fs::File::create(path)?;
+ cache.write_to_writer(file)?;
+ }
+ Some(("create-java-lib", sub_matches)) => {
+ let path = get_required_arg::<String>(sub_matches, "cache")?;
+ let file = fs::File::open(path)?;
+ let cache = Cache::read_from_reader(file)?;
+ let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
+ let generated_file = commands::create_java_lib(&cache)?;
+ write_output_file_realtive_to_dir(&dir, &generated_file)?;
+ }
+ Some(("create-cpp-lib", sub_matches)) => {
+ let path = get_required_arg::<String>(sub_matches, "cache")?;
+ let file = fs::File::open(path)?;
+ let cache = Cache::read_from_reader(file)?;
+ let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
+ let generated_file = commands::create_cpp_lib(&cache)?;
+ write_output_file_realtive_to_dir(&dir, &generated_file)?;
+ }
+ Some(("dump", sub_matches)) => {
+ let path = get_required_arg::<String>(sub_matches, "cache")?;
+ let file = fs::File::open(path)?;
+ let cache = Cache::read_from_reader(file)?;
+ let format = get_required_arg::<DumpFormat>(sub_matches, "format")?;
+ let output = commands::dump_cache(cache, *format)?;
+ let path = get_required_arg::<String>(sub_matches, "out")?;
+ let mut file: Box<dyn Write> = if *path == "-" {
+ Box::new(io::stdout())
+ } else {
+ Box::new(fs::File::create(path)?)
+ };
+ file.write_all(&output)?;
+ }
+ _ => unreachable!(),
+ }
+ Ok(())
+}
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
new file mode 100644
index 0000000..cb75692
--- /dev/null
+++ b/tools/aconfig/src/protos.rs
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+// 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"))]
+pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
+
+// ---- When building with cargo ----
+#[cfg(feature = "cargo")]
+include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_value as ProtoFlagValue;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_values as ProtoFlagValues;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_permission as ProtoFlagPermission;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Flag_state as ProtoFlagState;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Tracepoint as ProtoTracepoint;
+
+// ---- Common for both the Android tool-chain and cargo ----
+use anyhow::Result;
+
+pub fn try_from_text_proto<T>(s: &str) -> Result<T>
+where
+ T: protobuf::MessageFull,
+{
+ // warning: parse_from_str does not check if required fields are set
+ protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
+}
diff --git a/tools/aconfig/templates/cpp.template b/tools/aconfig/templates/cpp.template
new file mode 100644
index 0000000..ae8b59f
--- /dev/null
+++ b/tools/aconfig/templates/cpp.template
@@ -0,0 +1,25 @@
+#ifndef {namespace}_HEADER_H
+#define {namespace}_HEADER_H
+#include "{namespace}.h"
+{{ if readwrite }}
+#include <server_configurable_flags/get_flags.h>
+using namespace server_configurable_flags;
+{{ endif }}
+namespace {namespace} \{
+ {{ for item in class_elements}}
+ class {item.flag_name} \{
+ public:
+ virtual const bool value() \{
+ {{ if item.readwrite- }}
+ return GetServerConfigurableFlag(
+ "{namespace}",
+ "{item.flag_name}",
+ "{item.default_value}") == "true";
+ {{ -else- }}
+ return {item.default_value};
+ {{ -endif }}
+ }
+ }
+ {{ endfor }}
+}
+#endif
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
new file mode 100644
index 0000000..89da18b
--- /dev/null
+++ b/tools/aconfig/templates/java.template
@@ -0,0 +1,19 @@
+package {namespace};
+{{ if readwrite }}
+import android.provider.DeviceConfig;
+{{ endif }}
+public final class Flags \{
+ {{ for item in class_elements}}
+ public static boolean {item.method_name}() \{
+ {{ if item.readwrite- }}
+ return DeviceConfig.getBoolean(
+ "{namespace}",
+ "{item.feature_name}__{item.flag_name}",
+ {item.default_value}
+ );
+ {{ -else- }}
+ return {item.default_value};
+ {{ -endif }}
+ }
+ {{ endfor }}
+}
diff --git a/tools/compliance/cmd/sbom/sbom.go b/tools/compliance/cmd/sbom/sbom.go
index c378e39..f61289e 100644
--- a/tools/compliance/cmd/sbom/sbom.go
+++ b/tools/compliance/cmd/sbom/sbom.go
@@ -55,6 +55,7 @@
product string
stripPrefix []string
creationTime creationTimeGetter
+ buildid string
}
func (ctx context) strip(installPath string) string {
@@ -124,6 +125,7 @@
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)")
+ buildid := flags.String("build_id", "", "Uniquely identifies the build. (default timestamp)")
flags.Parse(expandedArgs)
@@ -162,7 +164,7 @@
ofile = obuf
}
- ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, actualTime}
+ ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, actualTime, *buildid}
spdxDoc, deps, err := sbomGenerator(ctx, flags.Args()...)
@@ -317,14 +319,21 @@
}
// generateSPDXNamespace generates a unique SPDX Document Namespace using a SHA1 checksum
-// and the CreationInfo.Created field as the date.
-func generateSPDXNamespace(created string) string {
- // Compute a SHA1 checksum of the CreationInfo.Created field.
- hash := sha1.Sum([]byte(created))
- checksum := hex.EncodeToString(hash[:])
+func generateSPDXNamespace(buildid string, created string, files ...string) string {
- // Combine the checksum and timestamp to generate the SPDX Namespace.
- namespace := fmt.Sprintf("SPDXRef-DOCUMENT-%s-%s", created, checksum)
+ seed := strings.Join(files, "")
+
+ if buildid == "" {
+ seed += created
+ } else {
+ seed += buildid
+ }
+
+ // Compute a SHA1 checksum of the seed.
+ hash := sha1.Sum([]byte(seed))
+ uuid := hex.EncodeToString(hash[:])
+
+ namespace := fmt.Sprintf("SPDXRef-DOCUMENT-%s", uuid)
return namespace
}
@@ -523,7 +532,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: docName,
- DocumentNamespace: generateSPDXNamespace(ci.Created),
+ DocumentNamespace: generateSPDXNamespace(ctx.buildid, ci.Created, files...),
CreationInfo: ci,
Packages: pkgs,
Relationships: relationships,
diff --git a/tools/compliance/cmd/sbom/sbom_test.go b/tools/compliance/cmd/sbom/sbom_test.go
index 6472f51..8a62713 100644
--- a/tools/compliance/cmd/sbom/sbom_test.go
+++ b/tools/compliance/cmd/sbom/sbom_test.go
@@ -59,7 +59,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-firstparty-highest.apex",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/highest.apex.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -187,7 +187,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-firstparty-application",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/application.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -266,7 +266,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-firstparty-container.zip",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/container.zip.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -394,7 +394,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-firstparty-bin-bin1",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/bin/bin1.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -460,7 +460,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-firstparty-lib-libd.so",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/lib/libd.so.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -500,7 +500,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-notice-highest.apex",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/highest.apex.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -634,7 +634,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-notice-container.zip",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/container.zip.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -768,7 +768,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-notice-application",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/application.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -853,7 +853,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-notice-bin-bin1",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/bin/bin1.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -925,7 +925,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-notice-lib-libd.so",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/lib/libd.so.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -965,7 +965,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-reciprocal-highest.apex",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/highest.apex.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1105,7 +1105,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-reciprocal-application",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/application.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1196,7 +1196,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-reciprocal-bin-bin1",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/bin/bin1.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1268,7 +1268,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-reciprocal-lib-libd.so",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/lib/libd.so.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1308,7 +1308,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-restricted-highest.apex",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/highest.apex.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1454,7 +1454,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-restricted-container.zip",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/container.zip.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1600,7 +1600,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-restricted-bin-bin1",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/bin/bin1.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1678,7 +1678,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-restricted-lib-libd.so",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/lib/libd.so.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1718,7 +1718,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-proprietary-highest.apex",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/highest.apex.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -1864,7 +1864,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-proprietary-container.zip",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/container.zip.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -2010,7 +2010,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-proprietary-application",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/application.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -2101,7 +2101,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-proprietary-bin-bin1",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/bin/bin1.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -2173,7 +2173,7 @@
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "testdata-proprietary-lib-libd.so",
- DocumentNamespace: generateSPDXNamespace("1970-01-01T00:00:00Z"),
+ DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/lib/libd.so.meta_lic"),
CreationInfo: getCreationInfo(t),
Packages: []*spdx.Package{
{
@@ -2215,7 +2215,7 @@
rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
}
- ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, fakeTime}
+ ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, fakeTime, ""}
spdxDoc, deps, err := sbomGenerator(&ctx, rootFiles...)
if err != nil {
@@ -2262,6 +2262,96 @@
}
}
+func TestGenerateSPDXNamespace(t *testing.T) {
+
+ buildID1 := "example-1"
+ buildID2 := "example-2"
+ files1 := "file1"
+ timestamp1 := "2022-05-01"
+ timestamp2 := "2022-05-02"
+ files2 := "file2"
+
+ // Test case 1: different timestamps, same files
+ nsh1 := generateSPDXNamespace("", timestamp1, files1)
+ nsh2 := generateSPDXNamespace("", timestamp2, files1)
+
+ if nsh1 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files1)
+ }
+
+ if nsh2 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp2, files1)
+ }
+
+ if nsh1 == nsh2 {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", "", timestamp1, files1, "", timestamp2, files1)
+ }
+
+ // Test case 2: different build ids, same timestamps and files
+ nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
+ nsh2 = generateSPDXNamespace(buildID2, timestamp1, files1)
+
+ if nsh1 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
+ }
+
+ if nsh2 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID2, timestamp1, files1)
+ }
+
+ if nsh1 == nsh2 {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", buildID1, timestamp1, files1, buildID2, timestamp1, files1)
+ }
+
+ // Test case 3: same build ids and files, different timestamps
+ nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
+ nsh2 = generateSPDXNamespace(buildID1, timestamp2, files1)
+
+ if nsh1 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
+ }
+
+ if nsh2 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp2, files1)
+ }
+
+ if nsh1 != nsh2 {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected same namespace hashes, but got different: %s and %s", buildID1, timestamp1, files1, buildID2, timestamp1, files1, nsh1, nsh2)
+ }
+
+ // Test case 4: same build ids and timestamps, different files
+ nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
+ nsh2 = generateSPDXNamespace(buildID1, timestamp1, files2)
+
+ if nsh1 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
+ }
+
+ if nsh2 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files2)
+ }
+
+ if nsh1 == nsh2 {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", buildID1, timestamp1, files1, buildID1, timestamp1, files2)
+ }
+
+ // Test case 5: empty build ids, same timestamps and different files
+ nsh1 = generateSPDXNamespace("", timestamp1, files1)
+ nsh2 = generateSPDXNamespace("", timestamp1, files2)
+
+ if nsh1 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files1)
+ }
+
+ if nsh2 == "" {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files2)
+ }
+
+ if nsh1 == nsh2 {
+ t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", "", timestamp1, files1, "", timestamp1, files2)
+ }
+}
+
func getCreationInfo(t *testing.T) *spdx.CreationInfo {
ci, err := builder2v2.BuildCreationInfoSection2_2("Organization", "Google LLC", nil)
if err != nil {
diff --git a/tools/finalization/environment.sh b/tools/finalization/environment.sh
index d95bea0..9714ac4 100755
--- a/tools/finalization/environment.sh
+++ b/tools/finalization/environment.sh
@@ -2,20 +2,23 @@
set -ex
-export FINAL_BUG_ID='275409981'
+export FINAL_BUG_ID='0' # CI only
-export FINAL_PLATFORM_CODENAME='UpsideDownCake'
-export CURRENT_PLATFORM_CODENAME='UpsideDownCake'
-export FINAL_PLATFORM_CODENAME_JAVA='UPSIDE_DOWN_CAKE'
-export FINAL_PLATFORM_SDK_VERSION='34'
-export FINAL_PLATFORM_VERSION='14'
+export FINAL_PLATFORM_CODENAME='VanillaIceCream'
+export CURRENT_PLATFORM_CODENAME='VanillaIceCream'
+export FINAL_PLATFORM_CODENAME_JAVA='VANILLA_ICE_CREAM'
+export FINAL_PLATFORM_VERSION='15'
-export FINAL_BUILD_PREFIX='UP1A'
-
-export FINAL_MAINLINE_EXTENSION='7'
+# Set arbitrary large values for CI.
+# SDK_VERSION needs to be <61 (lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ApiConstraint.kt)
+# There are multiple places where we rely on next SDK version to be previous + 1, e.g. RESOURCES_SDK_INT.
+# We might or might not fix this in future, but for now let's keep it +1.
+export FINAL_PLATFORM_SDK_VERSION='35'
+# Feel free to randomize once in a while to detect buggy version detection code.
+export FINAL_MAINLINE_EXTENSION='58'
# Options:
# 'unfinalized' - branch is in development state,
# 'sdk' - SDK/API is finalized
# 'rel' - branch is finalized, switched to REL
-export FINAL_STATE='rel'
+export FINAL_STATE='unfinalized'
diff --git a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
index 0491701..fa33986 100755
--- a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
+++ b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
@@ -4,13 +4,15 @@
function apply_droidstubs_hack() {
if ! grep -q 'STOPSHIP: RESTORE THIS LOGIC WHEN DECLARING "REL" BUILD' "$top/build/soong/java/droidstubs.go" ; then
- git -C "$top/build/soong" apply --allow-empty ../../build/make/tools/finalization/build_soong_java_droidstubs.go.apply_hack.diff
+ local build_soong_git_root="$(readlink -f $top/build/soong)"
+ git -C "$build_soong_git_root" apply --allow-empty ../../build/make/tools/finalization/build_soong_java_droidstubs.go.apply_hack.diff
fi
}
function apply_resources_sdk_int_fix() {
if ! grep -q 'public static final int RESOURCES_SDK_INT = SDK_INT;' "$top/frameworks/base/core/java/android/os/Build.java" ; then
- git -C "$top/frameworks/base" apply --allow-empty ../../build/make/tools/finalization/frameworks_base.apply_resource_sdk_int.diff
+ local base_git_root="$(readlink -f $top/frameworks/base)"
+ git -C "$base_git_root" apply --allow-empty ../../build/make/tools/finalization/frameworks_base.apply_resource_sdk_int.diff
fi
}
@@ -92,9 +94,7 @@
AIDL_TRANSITIVE_FREEZE=true $m aidl-freeze-api create_reference_dumps
# Generate ABI dumps
- ANDROID_BUILD_TOP="$top" \
- out/host/linux-x86/bin/create_reference_dumps \
- -p aosp_arm64 --build-variant user
+ ANDROID_BUILD_TOP="$top" out/host/linux-x86/bin/create_reference_dumps
echo "NOTE: THIS INTENTIONALLY MAY FAIL AND REPAIR ITSELF (until 'DONE')"
# Update new versions of files. See update-vndk-list.sh (which requires envsetup.sh)
@@ -107,6 +107,12 @@
# frameworks/libs/modules-utils
finalize_modules_utils
+ # development/sdk
+ local platform_source="$top/development/sdk/platform_source.prop_template"
+ sed -i -e 's/Pkg\.Revision.*/Pkg\.Revision=1/g' $platform_source
+ local build_tools_source="$top/development/sdk/build_tools_source.prop_template"
+ sed -i -e 's/Pkg\.Revision.*/Pkg\.Revision=${PLATFORM_SDK_VERSION}.0.0/g' $build_tools_source
+
# build/make
local version_defaults="$top/build/make/core/version_defaults.mk"
sed -i -e "s/PLATFORM_SDK_VERSION := .*/PLATFORM_SDK_VERSION := ${FINAL_PLATFORM_SDK_VERSION}/g" $version_defaults
@@ -114,10 +120,10 @@
sed -i -e "s/sepolicy_major_vers := .*/sepolicy_major_vers := ${FINAL_PLATFORM_SDK_VERSION}/g" "$top/build/make/core/config.mk"
cp "$top/build/make/target/product/gsi/current.txt" "$top/build/make/target/product/gsi/$FINAL_PLATFORM_SDK_VERSION.txt"
- # build/soong
- local codename_version="\"${FINAL_PLATFORM_CODENAME}\": ${FINAL_PLATFORM_SDK_VERSION}"
- if ! grep -q "$codename_version" "$top/build/soong/android/api_levels.go" ; then
- sed -i -e "/:.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\\t\t$codename_version," "$top/build/soong/android/api_levels.go"
+ # build/bazel
+ local codename_version="\"${FINAL_PLATFORM_CODENAME}\": ${FINAL_PLATFORM_SDK_VERSION}"
+ if ! grep -q "$codename_version" "$top/build/bazel/rules/common/api_constants.bzl" ; then
+ sed -i -e "/:.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\ $codename_version," "$top/build/bazel/rules/common/api_constants.bzl"
fi
# cts
diff --git a/tools/finalization/finalize-sdk-rel.sh b/tools/finalization/finalize-sdk-rel.sh
index 6cf4124..62e5ee5 100755
--- a/tools/finalization/finalize-sdk-rel.sh
+++ b/tools/finalization/finalize-sdk-rel.sh
@@ -34,7 +34,8 @@
revert_resources_sdk_int_fix
# build/make/core/version_defaults.mk
- sed -i -e "s/PLATFORM_VERSION_CODENAME.${FINAL_BUILD_PREFIX} := .*/PLATFORM_VERSION_CODENAME.${FINAL_BUILD_PREFIX} := REL/g" "$top/build/make/core/version_defaults.mk"
+ # Mark all versions "released".
+ sed -i 's/\(PLATFORM_VERSION_CODENAME\.[^[:space:]]*\) := [^[:space:]]*/\1 := REL/g' "$top/build/make/core/version_defaults.mk"
# cts
echo "$FINAL_PLATFORM_VERSION" > "$top/cts/tests/tests/os/assets/platform_versions.txt"
@@ -56,7 +57,7 @@
mkdir -p "$top/prebuilts/abi-dumps/platform/$FINAL_PLATFORM_SDK_VERSION"
cp -r "$top/prebuilts/abi-dumps/platform/current/64/" "$top/prebuilts/abi-dumps/platform/$FINAL_PLATFORM_SDK_VERSION/"
- if [ "$FINAL_STATE" != "sdk" ] ; then
+ if [ "$FINAL_STATE" != "sdk" ] || [ "$FINAL_PLATFORM_CODENAME" == "$CURRENT_PLATFORM_CODENAME" ] ; then
# prebuilts/abi-dumps/vndk
mv "$top/prebuilts/abi-dumps/vndk/$CURRENT_PLATFORM_CODENAME" "$top/prebuilts/abi-dumps/vndk/$FINAL_PLATFORM_SDK_VERSION"
fi;
diff --git a/tools/finalization/localonly-steps.sh b/tools/finalization/localonly-steps.sh
index 6107b3e..7318ca1 100755
--- a/tools/finalization/localonly-steps.sh
+++ b/tools/finalization/localonly-steps.sh
@@ -17,7 +17,7 @@
$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=sdk TARGET_BUILD_VARIANT=userdebug sdk dist sdk_repo DIST_DIR=out/dist
# Build Modules SDKs.
- TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh"
+ TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh" --build-release=latest
# Update prebuilts.
"$top/prebuilts/build-tools/path/linux-x86/python3" -W ignore::DeprecationWarning "$top/prebuilts/sdk/update_prebuilts.py" --local_mode -f ${FINAL_PLATFORM_SDK_VERSION} -e ${FINAL_MAINLINE_EXTENSION} --bug 1 1
diff --git a/tools/rbcrun/Android.bp b/tools/rbcrun/Android.bp
index fcc33ef..4fab858 100644
--- a/tools/rbcrun/Android.bp
+++ b/tools/rbcrun/Android.bp
@@ -34,6 +34,7 @@
pkgPath: "rbcrun",
deps: [
"go-starlark-starlark",
+ "go-starlark-starlarkjson",
"go-starlark-starlarkstruct",
"go-starlark-starlarktest",
],
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index 32afa45..a0fb9e1 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -24,13 +24,19 @@
"strings"
"go.starlark.net/starlark"
+ "go.starlark.net/starlarkjson"
"go.starlark.net/starlarkstruct"
)
-const callerDirKey = "callerDir"
+type ExecutionMode int
+const (
+ ExecutionModeRbc ExecutionMode = iota
+ ExecutionModeMake ExecutionMode = iota
+)
-var LoadPathRoot = "."
-var shellPath string
+const callerDirKey = "callerDir"
+const shellKey = "shell"
+const executionModeKey = "executionMode"
type modentry struct {
globals starlark.StringDict
@@ -39,20 +45,66 @@
var moduleCache = make(map[string]*modentry)
-var builtins starlark.StringDict
+var rbcBuiltins starlark.StringDict = starlark.StringDict{
+ "struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
+ // To convert find-copy-subdir and product-copy-files-by pattern
+ "rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
+ // To convert makefile's $(shell cmd)
+ "rblf_shell": starlark.NewBuiltin("rblf_shell", shell),
+ // Output to stderr
+ "rblf_log": starlark.NewBuiltin("rblf_log", log),
+ // To convert makefile's $(wildcard foo*)
+ "rblf_wildcard": starlark.NewBuiltin("rblf_wildcard", wildcard),
+}
-func moduleName2AbsPath(moduleName string, callerDir string) (string, error) {
- path := moduleName
- if ix := strings.LastIndex(path, ":"); ix >= 0 {
- path = path[0:ix] + string(os.PathSeparator) + path[ix+1:]
+var makeBuiltins starlark.StringDict = starlark.StringDict{
+ "struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
+ "json": starlarkjson.Module,
+}
+
+// Takes a module name (the first argument to the load() function) and returns the path
+// it's trying to load, stripping out leading //, and handling leading :s.
+func cleanModuleName(moduleName string, callerDir string) (string, error) {
+ if strings.Count(moduleName, ":") > 1 {
+ return "", fmt.Errorf("at most 1 colon must be present in starlark path: %s", moduleName)
}
- if strings.HasPrefix(path, "//") {
- return filepath.Abs(filepath.Join(LoadPathRoot, path[2:]))
+
+ // We don't have full support for external repositories, but at least support skylib's dicts.
+ if moduleName == "@bazel_skylib//lib:dicts.bzl" {
+ return "external/bazel-skylib/lib/dicts.bzl", nil
+ }
+
+ localLoad := false
+ if strings.HasPrefix(moduleName, "@//") {
+ moduleName = moduleName[3:]
+ } else if strings.HasPrefix(moduleName, "//") {
+ moduleName = moduleName[2:]
} else if strings.HasPrefix(moduleName, ":") {
- return filepath.Abs(filepath.Join(callerDir, path[1:]))
+ moduleName = moduleName[1:]
+ localLoad = true
} else {
- return filepath.Abs(path)
+ return "", fmt.Errorf("load path must start with // or :")
}
+
+ if ix := strings.LastIndex(moduleName, ":"); ix >= 0 {
+ moduleName = moduleName[:ix] + string(os.PathSeparator) + moduleName[ix+1:]
+ }
+
+ if filepath.Clean(moduleName) != moduleName {
+ return "", fmt.Errorf("load path must be clean, found: %s, expected: %s", moduleName, filepath.Clean(moduleName))
+ }
+ if strings.HasPrefix(moduleName, "../") {
+ return "", fmt.Errorf("load path must not start with ../: %s", moduleName)
+ }
+ if strings.HasPrefix(moduleName, "/") {
+ return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName)
+ }
+
+ if localLoad {
+ return filepath.Join(callerDir, moduleName), nil
+ }
+
+ return moduleName, nil
}
// loader implements load statement. The format of the loaded module URI is
@@ -61,14 +113,18 @@
// The presence of `|symbol` indicates that the loader should return a single 'symbol'
// bound to None if file is missing.
func loader(thread *starlark.Thread, module string) (starlark.StringDict, error) {
- pipePos := strings.LastIndex(module, "|")
- mustLoad := pipePos < 0
+ mode := thread.Local(executionModeKey).(ExecutionMode)
var defaultSymbol string
- if !mustLoad {
- defaultSymbol = module[pipePos+1:]
- module = module[:pipePos]
+ mustLoad := true
+ if mode == ExecutionModeRbc {
+ pipePos := strings.LastIndex(module, "|")
+ mustLoad = pipePos < 0
+ if !mustLoad {
+ defaultSymbol = module[pipePos+1:]
+ module = module[:pipePos]
+ }
}
- modulePath, err := moduleName2AbsPath(module, thread.Local(callerDirKey).(string))
+ modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string))
if err != nil {
return nil, err
}
@@ -100,8 +156,17 @@
}
childThread.SetLocal(callerDirKey, filepath.Dir(modulePath))
- globals, err := starlark.ExecFile(childThread, modulePath, nil, builtins)
- e = &modentry{globals, err}
+ childThread.SetLocal(shellKey, thread.Local(shellKey))
+ childThread.SetLocal(executionModeKey, mode)
+ if mode == ExecutionModeRbc {
+ globals, err := starlark.ExecFile(childThread, modulePath, nil, rbcBuiltins)
+ e = &modentry{globals, err}
+ } else if mode == ExecutionModeMake {
+ globals, err := starlark.ExecFile(childThread, modulePath, nil, makeBuiltins)
+ e = &modentry{globals, err}
+ } else {
+ return nil, fmt.Errorf("unknown executionMode %d", mode)
+ }
} else {
e = &modentry{starlark.StringDict{defaultSymbol: starlark.None}, nil}
}
@@ -189,12 +254,13 @@
// its output the same way as Make's $(shell ) function. The end-of-lines
// ("\n" or "\r\n") are replaced with " " in the result, and the trailing
// end-of-line is removed.
-func shell(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
+func shell(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
kwargs []starlark.Tuple) (starlark.Value, error) {
var command string
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &command); err != nil {
return starlark.None, err
}
+ shellPath := thread.Local(shellKey).(string)
if shellPath == "" {
return starlark.None,
fmt.Errorf("cannot run shell, /bin/sh is missing (running on Windows?)")
@@ -223,16 +289,6 @@
return starlark.NewList(elems)
}
-// propsetFromEnv constructs a propset from the array of KEY=value strings
-func structFromEnv(env []string) *starlarkstruct.Struct {
- sd := make(map[string]starlark.Value, len(env))
- for _, x := range env {
- kv := strings.SplitN(x, "=", 2)
- sd[kv[0]] = starlark.String(kv[1])
- }
- return starlarkstruct.FromStringDict(starlarkstruct.Default, sd)
-}
-
func log(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
sep := " "
if err := starlark.UnpackArgs("print", nil, kwargs, "sep?", &sep); err != nil {
@@ -255,50 +311,68 @@
return starlark.None, nil
}
-func setup(env []string) {
- // Create the symbols that aid makefile conversion. See README.md
- builtins = starlark.StringDict{
- "struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
- "rblf_cli": structFromEnv(env),
- "rblf_env": structFromEnv(os.Environ()),
- // To convert find-copy-subdir and product-copy-files-by pattern
- "rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
- // To convert makefile's $(shell cmd)
- "rblf_shell": starlark.NewBuiltin("rblf_shell", shell),
- // Output to stderr
- "rblf_log": starlark.NewBuiltin("rblf_log", log),
- // To convert makefile's $(wildcard foo*)
- "rblf_wildcard": starlark.NewBuiltin("rblf_wildcard", wildcard),
- }
-
- // NOTE(asmundak): OS-specific. Behave similar to Linux `system` call,
- // which always uses /bin/sh to run the command
- shellPath = "/bin/sh"
- if _, err := os.Stat(shellPath); err != nil {
- shellPath = ""
- }
-}
-
// Parses, resolves, and executes a Starlark file.
// filename and src parameters are as for starlark.ExecFile:
// * filename is the name of the file to execute,
// and the name that appears in error messages;
// * src is an optional source of bytes to use instead of filename
// (it can be a string, or a byte array, or an io.Reader instance)
-// * commandVars is an array of "VAR=value" items. They are accessible from
-// the starlark script as members of the `rblf_cli` propset.
-func Run(filename string, src interface{}, commandVars []string) error {
- setup(commandVars)
+// Returns the top-level starlark variables, the list of starlark files loaded, and an error
+func Run(filename string, src interface{}, mode ExecutionMode) (starlark.StringDict, []string, error) {
+ // NOTE(asmundak): OS-specific. Behave similar to Linux `system` call,
+ // which always uses /bin/sh to run the command
+ shellPath := "/bin/sh"
+ if _, err := os.Stat(shellPath); err != nil {
+ shellPath = ""
+ }
mainThread := &starlark.Thread{
Name: "main",
- Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
+ Print: func(_ *starlark.Thread, msg string) {
+ if mode == ExecutionModeRbc {
+ // In rbc mode, rblf_log is used to print to stderr
+ fmt.Println(msg)
+ } else if mode == ExecutionModeMake {
+ fmt.Fprintln(os.Stderr, msg)
+ }
+ },
Load: loader,
}
- absPath, err := filepath.Abs(filename)
- if err == nil {
- mainThread.SetLocal(callerDirKey, filepath.Dir(absPath))
- _, err = starlark.ExecFile(mainThread, absPath, src, builtins)
+ filename, err := filepath.Abs(filename)
+ if err != nil {
+ return nil, nil, err
}
- return err
+ if wd, err := os.Getwd(); err == nil {
+ filename, err = filepath.Rel(wd, filename)
+ if err != nil {
+ return nil, nil, err
+ }
+ if strings.HasPrefix(filename, "../") {
+ return nil, nil, fmt.Errorf("path could not be made relative to workspace root: %s", filename)
+ }
+ } else {
+ return nil, nil, err
+ }
+
+ // Add top-level file to cache for cycle detection purposes
+ moduleCache[filename] = nil
+
+ var results starlark.StringDict
+ mainThread.SetLocal(callerDirKey, filepath.Dir(filename))
+ mainThread.SetLocal(shellKey, shellPath)
+ mainThread.SetLocal(executionModeKey, mode)
+ if mode == ExecutionModeRbc {
+ results, err = starlark.ExecFile(mainThread, filename, src, rbcBuiltins)
+ } else if mode == ExecutionModeMake {
+ results, err = starlark.ExecFile(mainThread, filename, src, makeBuiltins)
+ } else {
+ return results, nil, fmt.Errorf("unknown executionMode %d", mode)
+ }
+ loadedStarlarkFiles := make([]string, 0, len(moduleCache))
+ for file := range moduleCache {
+ loadedStarlarkFiles = append(loadedStarlarkFiles, file)
+ }
+ sort.Strings(loadedStarlarkFiles)
+
+ return results, loadedStarlarkFiles, err
}
diff --git a/tools/rbcrun/host_test.go b/tools/rbcrun/host_test.go
index 97f6ce9..10cac62 100644
--- a/tools/rbcrun/host_test.go
+++ b/tools/rbcrun/host_test.go
@@ -53,8 +53,7 @@
}
// Common setup for the tests: create thread, change to the test directory
-func testSetup(t *testing.T, env []string) *starlark.Thread {
- setup(env)
+func testSetup(t *testing.T) *starlark.Thread {
thread := &starlark.Thread{
Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
if module == "assert.star" {
@@ -72,14 +71,15 @@
func dataDir() string {
_, thisSrcFile, _, _ := runtime.Caller(0)
return filepath.Join(filepath.Dir(thisSrcFile), "testdata")
-
}
func exerciseStarlarkTestFile(t *testing.T, starFile string) {
// In order to use "assert.star" from go/starlark.net/starlarktest in the tests, provide:
// * load function that handles "assert.star"
// * starlarktest.DataFile function that finds its location
- setup(nil)
+ if err := os.Chdir(dataDir()); err != nil {
+ t.Fatal(err)
+ }
thread := &starlark.Thread{
Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
if module == "assert.star" {
@@ -90,21 +90,9 @@
starlarktest.SetReporter(thread, t)
_, thisSrcFile, _, _ := runtime.Caller(0)
filename := filepath.Join(filepath.Dir(thisSrcFile), starFile)
- if _, err := starlark.ExecFile(thread, filename, nil, builtins); err != nil {
- if err, ok := err.(*starlark.EvalError); ok {
- t.Fatal(err.Backtrace())
- }
- t.Fatal(err)
- }
-}
-
-func TestCliAndEnv(t *testing.T) {
- // TODO(asmundak): convert this to use exerciseStarlarkTestFile
- if err := os.Setenv("TEST_ENVIRONMENT_FOO", "test_environment_foo"); err != nil {
- t.Fatal(err)
- }
- thread := testSetup(t, []string{"CLI_FOO=foo"})
- if _, err := starlark.ExecFile(thread, "cli_and_env.star", nil, builtins); err != nil {
+ thread.SetLocal(executionModeKey, ExecutionModeRbc)
+ thread.SetLocal(shellKey, "/bin/sh")
+ if _, err := starlark.ExecFile(thread, filename, nil, rbcBuiltins); err != nil {
if err, ok := err.(*starlark.EvalError); ok {
t.Fatal(err.Backtrace())
}
@@ -114,11 +102,8 @@
func TestFileOps(t *testing.T) {
// TODO(asmundak): convert this to use exerciseStarlarkTestFile
- if err := os.Setenv("TEST_DATA_DIR", dataDir()); err != nil {
- t.Fatal(err)
- }
- thread := testSetup(t, nil)
- if _, err := starlark.ExecFile(thread, "file_ops.star", nil, builtins); err != nil {
+ thread := testSetup(t)
+ if _, err := starlark.ExecFile(thread, "file_ops.star", nil, rbcBuiltins); err != nil {
if err, ok := err.(*starlark.EvalError); ok {
t.Fatal(err.Backtrace())
}
@@ -128,7 +113,7 @@
func TestLoad(t *testing.T) {
// TODO(asmundak): convert this to use exerciseStarlarkTestFile
- thread := testSetup(t, nil)
+ thread := testSetup(t)
thread.Load = func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
if module == "assert.star" {
return starlarktest.LoadAssertModule()
@@ -137,9 +122,12 @@
}
}
dir := dataDir()
+ if err := os.Chdir(filepath.Dir(dir)); err != nil {
+ t.Fatal(err)
+ }
thread.SetLocal(callerDirKey, dir)
- LoadPathRoot = filepath.Dir(dir)
- if _, err := starlark.ExecFile(thread, "load.star", nil, builtins); err != nil {
+ thread.SetLocal(executionModeKey, ExecutionModeRbc)
+ if _, err := starlark.ExecFile(thread, "testdata/load.star", nil, rbcBuiltins); err != nil {
if err, ok := err.(*starlark.EvalError); ok {
t.Fatal(err.Backtrace())
}
@@ -148,8 +136,5 @@
}
func TestShell(t *testing.T) {
- if err := os.Setenv("TEST_DATA_DIR", dataDir()); err != nil {
- t.Fatal(err)
- }
exerciseStarlarkTestFile(t, "testdata/shell.star")
}
diff --git a/tools/rbcrun/rbcrun/rbcrun.go b/tools/rbcrun/rbcrun/rbcrun.go
index 4db6a0b..b5182f0 100644
--- a/tools/rbcrun/rbcrun/rbcrun.go
+++ b/tools/rbcrun/rbcrun/rbcrun.go
@@ -17,52 +17,137 @@
import (
"flag"
"fmt"
- "go.starlark.net/starlark"
"os"
"rbcrun"
+ "regexp"
"strings"
+
+ "go.starlark.net/starlark"
)
var (
- execprog = flag.String("c", "", "execute program `prog`")
+ modeFlag = flag.String("mode", "", "the general behavior of rbcrun. Can be \"rbc\" or \"make\". Required.")
rootdir = flag.String("d", ".", "the value of // for load paths")
- file = flag.String("f", "", "file to execute")
perfFile = flag.String("perf", "", "save performance data")
+ identifierRe = regexp.MustCompile("[a-zA-Z_][a-zA-Z0-9_]*")
)
-func main() {
- flag.Parse()
- filename := *file
- var src interface{}
- var env []string
+func getEntrypointStarlarkFile() string {
+ filename := ""
- rc := 0
for _, arg := range flag.Args() {
- if strings.Contains(arg, "=") {
- env = append(env, arg)
- } else if filename == "" {
+ if filename == "" {
filename = arg
} else {
quit("only one file can be executed\n")
}
}
- if *execprog != "" {
- if filename != "" {
- quit("either -c or file name should be present\n")
- }
- filename = "<cmdline>"
- src = *execprog
- }
if filename == "" {
- if len(env) > 0 {
- fmt.Fprintln(os.Stderr,
- "no file to run -- if your file's name contains '=', use -f to specify it")
- }
flag.Usage()
os.Exit(1)
}
- if stat, err := os.Stat(*rootdir); os.IsNotExist(err) || !stat.IsDir() {
- quit("%s is not a directory\n", *rootdir)
+ return filename
+}
+
+func getMode() rbcrun.ExecutionMode {
+ switch *modeFlag {
+ case "rbc":
+ return rbcrun.ExecutionModeRbc
+ case "make":
+ return rbcrun.ExecutionModeMake
+ case "":
+ quit("-mode flag is required.")
+ default:
+ quit("Unknown -mode value %q, expected 1 of \"rbc\", \"make\"", *modeFlag)
+ }
+ return rbcrun.ExecutionModeMake
+}
+
+var makeStringReplacer = strings.NewReplacer("#", "\\#", "$", "$$")
+
+func cleanStringForMake(s string) (string, error) {
+ if strings.ContainsAny(s, "\\\n") {
+ // \\ in make is literally \\, not a single \, so we can't allow them.
+ // \<newline> in make will produce a space, not a newline.
+ return "", fmt.Errorf("starlark strings exported to make cannot contain backslashes or newlines")
+ }
+ return makeStringReplacer.Replace(s), nil
+}
+
+func getValueInMakeFormat(value starlark.Value, allowLists bool) (string, error) {
+ switch v := value.(type) {
+ case starlark.String:
+ if cleanedValue, err := cleanStringForMake(v.GoString()); err == nil {
+ return cleanedValue, nil
+ } else {
+ return "", err
+ }
+ case starlark.Int:
+ return v.String(), nil
+ case *starlark.List:
+ if !allowLists {
+ return "", fmt.Errorf("nested lists are not allowed to be exported from starlark to make, flatten the list in starlark first")
+ }
+ result := ""
+ for i := 0; i < v.Len(); i++ {
+ value, err := getValueInMakeFormat(v.Index(i), false)
+ if err != nil {
+ return "", err
+ }
+ if i > 0 {
+ result += " "
+ }
+ result += value
+ }
+ return result, nil
+ default:
+ return "", fmt.Errorf("only starlark strings, ints, and lists of strings/ints can be exported to make. Please convert all other types in starlark first. Found type: %s", value.Type())
+ }
+}
+
+func printVarsInMakeFormat(globals starlark.StringDict) error {
+ // We could just directly export top level variables by name instead of going through
+ // a variables_to_export_to_make dictionary, but that wouldn't allow for exporting a
+ // runtime-defined number of variables to make. This can be important because dictionaries
+ // in make are often represented by a unique variable for every key in the dictionary.
+ variablesValue, ok := globals["variables_to_export_to_make"]
+ if !ok {
+ return fmt.Errorf("expected top-level starlark file to have a \"variables_to_export_to_make\" variable")
+ }
+ variables, ok := variablesValue.(*starlark.Dict)
+ if !ok {
+ return fmt.Errorf("expected variables_to_export_to_make to be a dict, got %s", variablesValue.Type())
+ }
+
+ for _, varTuple := range variables.Items() {
+ varNameStarlark, ok := varTuple.Index(0).(starlark.String)
+ if !ok {
+ return fmt.Errorf("all keys in variables_to_export_to_make must be strings, but got %q", varTuple.Index(0).Type())
+ }
+ varName := varNameStarlark.GoString()
+ if !identifierRe.MatchString(varName) {
+ return fmt.Errorf("all variables at the top level starlark file must be valid c identifiers, but got %q", varName)
+ }
+ if varName == "LOADED_STARLARK_FILES" {
+ return fmt.Errorf("the name LOADED_STARLARK_FILES is reserved for use by the starlark interpreter")
+ }
+ valueMake, err := getValueInMakeFormat(varTuple.Index(1), true)
+ if err != nil {
+ return err
+ }
+ // The :=$= is special Kati syntax that means "set and make readonly"
+ fmt.Printf("%s :=$= %s\n", varName, valueMake)
+ }
+ return nil
+}
+
+func main() {
+ flag.Parse()
+ filename := getEntrypointStarlarkFile()
+ mode := getMode()
+
+ if os.Chdir(*rootdir) != nil {
+ quit("could not chdir to %s\n", *rootdir)
}
if *perfFile != "" {
pprof, err := os.Create(*perfFile)
@@ -74,8 +159,8 @@
quit("%s\n", err)
}
}
- rbcrun.LoadPathRoot = *rootdir
- err := rbcrun.Run(filename, src, env)
+ variables, loadedStarlarkFiles, err := rbcrun.Run(filename, nil, mode)
+ rc := 0
if *perfFile != "" {
if err2 := starlark.StopProfile(); err2 != nil {
fmt.Fprintln(os.Stderr, err2)
@@ -89,6 +174,12 @@
quit("%s\n", err)
}
}
+ if mode == rbcrun.ExecutionModeMake {
+ if err := printVarsInMakeFormat(variables); err != nil {
+ quit("%s\n", err)
+ }
+ fmt.Printf("LOADED_STARLARK_FILES := %s\n", strings.Join(loadedStarlarkFiles, " "))
+ }
os.Exit(rc)
}
diff --git a/tools/rbcrun/testdata/cli_and_env.star b/tools/rbcrun/testdata/cli_and_env.star
deleted file mode 100644
index d6f464a..0000000
--- a/tools/rbcrun/testdata/cli_and_env.star
+++ /dev/null
@@ -1,11 +0,0 @@
-# Tests rblf_env access
-load("assert.star", "assert")
-
-
-def test():
- assert.eq(rblf_env.TEST_ENVIRONMENT_FOO, "test_environment_foo")
- assert.fails(lambda: rblf_env.FOO_BAR_BAZ, ".*struct has no .FOO_BAR_BAZ attribute$")
- assert.eq(rblf_cli.CLI_FOO, "foo")
-
-
-test()
diff --git a/tools/rbcrun/testdata/file_ops.star b/tools/rbcrun/testdata/file_ops.star
index 2ee78fc..b2b907c 100644
--- a/tools/rbcrun/testdata/file_ops.star
+++ b/tools/rbcrun/testdata/file_ops.star
@@ -1,22 +1,21 @@
# Tests file ops builtins
load("assert.star", "assert")
-
def test():
myname = "file_ops.star"
files = rblf_wildcard("*.star")
assert.true(myname in files, "expected %s in %s" % (myname, files))
- files = rblf_wildcard("*.star", rblf_env.TEST_DATA_DIR)
+ files = rblf_wildcard("*.star")
assert.true(myname in files, "expected %s in %s" % (myname, files))
files = rblf_wildcard("*.xxx")
assert.true(len(files) == 0, "expansion should be empty but contains %s" % files)
mydir = "testdata"
myrelname = "%s/%s" % (mydir, myname)
- files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*")
+ files = rblf_find_files("../", "*")
assert.true(mydir in files and myrelname in files, "expected %s and %s in %s" % (mydir, myrelname, files))
- files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*", only_files=1)
+ files = rblf_find_files("../", "*", only_files=1)
assert.true(mydir not in files, "did not expect %s in %s" % (mydir, files))
assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
- files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*.star")
+ files = rblf_find_files("../", "*.star")
assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
test()
diff --git a/tools/rbcrun/testdata/module1.star b/tools/rbcrun/testdata/module1.star
index be04f75..02919a0 100644
--- a/tools/rbcrun/testdata/module1.star
+++ b/tools/rbcrun/testdata/module1.star
@@ -2,6 +2,6 @@
load("assert.star", "assert")
# Make sure that builtins are defined for the loaded module, too
-assert.true(rblf_wildcard("module1.star"))
-assert.true(not rblf_wildcard("no_such file"))
+assert.true(rblf_wildcard("testdata/module1.star"))
+assert.true(not rblf_wildcard("testdata/no_such file"))
test = "module1"
diff --git a/tools/rbcrun/testdata/shell.star b/tools/rbcrun/testdata/shell.star
index ad10697..dd17375 100644
--- a/tools/rbcrun/testdata/shell.star
+++ b/tools/rbcrun/testdata/shell.star
@@ -1,5 +1,5 @@
# Tests "queue" data type
load("assert.star", "assert")
-assert.eq("load.star shell.star", rblf_shell("cd %s && ls -1 shell.star load.star 2>&1" % rblf_env.TEST_DATA_DIR))
-assert.eq("shell.star", rblf_shell("cd %s && echo shell.sta*" % rblf_env.TEST_DATA_DIR))
+assert.eq("load.star shell.star", rblf_shell("ls -1 shell.star load.star 2>&1"))
+assert.eq("shell.star", rblf_shell("echo shell.sta*"))
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index ac3271b..8d660f8 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -65,7 +65,7 @@
import ota_metadata_pb2
import rangelib
import sparse_img
-
+from concurrent.futures import ThreadPoolExecutor
from apex_utils import GetApexInfoFromTargetFiles
from common import ZipDelete, PARTITIONS_WITH_CARE_MAP, ExternalError, RunAndCheckOutput, IsSparseImage, MakeTempFile, ZipWrite
@@ -1083,8 +1083,15 @@
("system_dlkm", has_system_dlkm, AddSystemDlkm, []),
("system_other", has_system_other, AddSystemOther, []),
)
- for call in add_partition_calls:
- add_partition(*call)
+ # If output_zip exists, each add_partition_calls writes bytes to the same output_zip,
+ # which is not thread-safe. So, run them in serial if output_zip exists.
+ if output_zip:
+ for call in add_partition_calls:
+ add_partition(*call)
+ else:
+ with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:
+ for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:
+ future.result()
AddApexInfo(output_zip)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index b50caaa..b19438e 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -450,10 +450,7 @@
@property
def is_vabc(self):
- vendor_prop = self.info_dict.get("vendor.build.prop")
- vabc_enabled = vendor_prop and \
- vendor_prop.GetProp("ro.virtual_ab.compression.enabled") == "true"
- return vabc_enabled
+ return self.info_dict.get("virtual_ab_compression") == "true"
@property
def is_android_r(self):
@@ -474,9 +471,9 @@
for prop in props:
value = vendor_prop.GetProp(prop)
try:
- return int(value)
+ return int(value)
except:
- pass
+ pass
return -1
@property
@@ -757,6 +754,33 @@
return ReadBytesFromInputFile(input_file, fn).decode()
+def WriteBytesToInputFile(input_file, fn, data):
+ """Write bytes |data| contents to fn of input zipfile or directory."""
+ if isinstance(input_file, zipfile.ZipFile):
+ with input_file.open(fn, "w") as entry_fp:
+ return entry_fp.write(data)
+ elif zipfile.is_zipfile(input_file):
+ with zipfile.ZipFile(input_file, "r", allowZip64=True) as zfp:
+ with zfp.open(fn, "w") as entry_fp:
+ return entry_fp.write(data)
+ else:
+ if not os.path.isdir(input_file):
+ raise ValueError(
+ "Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: " + input_file)
+ path = os.path.join(input_file, *fn.split("/"))
+ try:
+ with open(path, "wb") as f:
+ return f.write(data)
+ except IOError as e:
+ if e.errno == errno.ENOENT:
+ raise KeyError(fn)
+
+
+def WriteToInputFile(input_file, fn, str: str):
+ """Write str content to fn of input file or directory"""
+ return WriteBytesToInputFile(input_file, fn, str.encode())
+
+
def ExtractFromInputFile(input_file, fn):
"""Extracts the contents of fn from input zipfile or directory into a file."""
if isinstance(input_file, zipfile.ZipFile):
@@ -1396,7 +1420,8 @@
def AppendAVBSigningArgs(cmd, partition):
"""Append signing arguments for avbtool."""
# e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
- key_path = ResolveAVBSigningPathArgs(OPTIONS.info_dict.get("avb_" + partition + "_key_path"))
+ key_path = ResolveAVBSigningPathArgs(
+ OPTIONS.info_dict.get("avb_" + partition + "_key_path"))
algorithm = OPTIONS.info_dict.get("avb_" + partition + "_algorithm")
if key_path and algorithm:
cmd.extend(["--key", key_path, "--algorithm", algorithm])
@@ -1415,7 +1440,7 @@
if os.path.exists(new_path):
return new_path
raise ExternalError(
- "Failed to find {}".format(new_path))
+ "Failed to find {}".format(new_path))
if not split_args:
return split_args
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index f8bdd81..7c27ef7 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -118,6 +118,7 @@
entries = [
'OTA/android-info.txt:android-info.txt',
+ 'META/fastboot-info.txt:fastboot-info.txt',
]
with zipfile.ZipFile(input_file) as input_zip:
namelist = input_zip.namelist()
diff --git a/tools/releasetools/non_ab_ota.py b/tools/releasetools/non_ab_ota.py
index c4fd809..667891c 100644
--- a/tools/releasetools/non_ab_ota.py
+++ b/tools/releasetools/non_ab_ota.py
@@ -23,6 +23,7 @@
from check_target_files_vintf import CheckVintfIfTrebleEnabled, HasPartition
from common import OPTIONS
from ota_utils import UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata, PropertyFiles
+import subprocess
logger = logging.getLogger(__name__)
@@ -277,7 +278,8 @@
needed_property_files = (
NonAbOtaPropertyFiles(),
)
- FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
+ FinalizeMetadata(metadata, staging_file, output_file,
+ needed_property_files, package_key=OPTIONS.package_key)
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
@@ -532,7 +534,8 @@
needed_property_files = (
NonAbOtaPropertyFiles(),
)
- FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
+ FinalizeMetadata(metadata, staging_file, output_file,
+ needed_property_files, package_key=OPTIONS.package_key)
def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
@@ -555,8 +558,18 @@
if OPTIONS.extracted_input is not None:
OPTIONS.input_tmp = OPTIONS.extracted_input
else:
- logger.info("unzipping target target-files...")
- OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
+ if not os.path.isdir(target_file):
+ logger.info("unzipping target target-files...")
+ OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
+ else:
+ OPTIONS.input_tmp = target_file
+ tmpfile = common.MakeTempFile(suffix=".zip")
+ os.unlink(tmpfile)
+ common.RunAndCheckOutput(
+ ["zip", tmpfile, "-r", ".", "-0"], cwd=target_file)
+ assert zipfile.is_zipfile(tmpfile)
+ target_file = tmpfile
+
OPTIONS.target_tmp = OPTIONS.input_tmp
# If the caller explicitly specified the device-specific extensions path via
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index e40256c..afbe81a 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -270,10 +270,9 @@
import common
import ota_utils
from ota_utils import (UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata,
- PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, CopyTargetFilesDir)
+ PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir)
from common import DoesInputFileContain, IsSparseImage
import target_files_diff
-from check_target_files_vintf import CheckVintfIfTrebleEnabled
from non_ab_ota import GenerateNonAbOtaPackage
from payload_signer import PayloadSigner
@@ -519,20 +518,14 @@
Returns:
The filename of target-files.zip that doesn't contain postinstall config.
"""
- # We should only make a copy if postinstall_config entry exists.
- with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
- if POSTINSTALL_CONFIG not in input_zip.namelist():
- return input_file
-
- target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
- shutil.copyfile(input_file, target_file)
- common.ZipDelete(target_file, POSTINSTALL_CONFIG)
- return target_file
+ config_path = os.path.join(input_file, POSTINSTALL_CONFIG)
+ if os.path.exists(config_path):
+ os.unlink(config_path)
+ return input_file
def ParseInfoDict(target_file_path):
- with zipfile.ZipFile(target_file_path, 'r', allowZip64=True) as zfp:
- return common.LoadInfoDict(zfp)
+ return common.LoadInfoDict(target_file_path)
def GetTargetFilesZipForCustomVABCCompression(input_file, vabc_compression_param):
@@ -544,6 +537,17 @@
Returns:
The path to modified target-files.zip
"""
+ if os.path.isdir(input_file):
+ dynamic_partition_info_path = os.path.join(
+ input_file, "META", "dynamic_partitions_info.txt")
+ with open(dynamic_partition_info_path, "r") as fp:
+ dynamic_partition_info = fp.read()
+ dynamic_partition_info = ModifyVABCCompressionParam(
+ dynamic_partition_info, vabc_compression_param)
+ with open(dynamic_partition_info_path, "w") as fp:
+ fp.write(dynamic_partition_info)
+ return input_file
+
target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
shutil.copyfile(input_file, target_file)
common.ZipDelete(target_file, DYNAMIC_PARTITION_INFO)
@@ -571,23 +575,7 @@
The filename of target-files.zip used for partial ota update.
"""
- def AddImageForPartition(partition_name):
- """Add the archive name for a given partition to the copy list."""
- for prefix in ['IMAGES', 'RADIO']:
- image_path = '{}/{}.img'.format(prefix, partition_name)
- if image_path in namelist:
- copy_entries.append(image_path)
- map_path = '{}/{}.map'.format(prefix, partition_name)
- if map_path in namelist:
- copy_entries.append(map_path)
- return
-
- raise ValueError("Cannot find {} in input zipfile".format(partition_name))
-
- with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
- original_ab_partitions = input_zip.read(
- AB_PARTITIONS).decode().splitlines()
- namelist = input_zip.namelist()
+ original_ab_partitions = common.ReadFromInputFile(input_file, AB_PARTITIONS)
unrecognized_partitions = [partition for partition in ab_partitions if
partition not in original_ab_partitions]
@@ -596,50 +584,65 @@
unrecognized_partitions)
logger.info("Generating partial updates for %s", ab_partitions)
+ for subdir in ["IMAGES", "RADIO", "PREBUILT_IMAGES"]:
+ image_dir = os.path.join(subdir)
+ if not os.path.exists(image_dir):
+ continue
+ for filename in os.listdir(image_dir):
+ filepath = os.path.join(image_dir, filename)
+ if filename.endswith(".img"):
+ partition_name = filename.removesuffix(".img")
+ if partition_name not in ab_partitions:
+ os.unlink(filepath)
- copy_entries = ['META/update_engine_config.txt']
- for partition_name in ab_partitions:
- AddImageForPartition(partition_name)
+ common.WriteToInputFile(input_file, 'META/ab_partitions.txt',
+ '\n'.join(ab_partitions))
+ CARE_MAP_ENTRY = "META/care_map.pb"
+ if DoesInputFileContain(input_file, CARE_MAP_ENTRY):
+ caremap = care_map_pb2.CareMap()
+ caremap.ParseFromString(
+ common.ReadBytesFromInputFile(input_file, CARE_MAP_ENTRY))
+ filtered = [
+ part for part in caremap.partitions if part.name in ab_partitions]
+ del caremap.partitions[:]
+ caremap.partitions.extend(filtered)
+ common.WriteBytesToInputFile(input_file, CARE_MAP_ENTRY,
+ caremap.SerializeToString())
- # Use zip2zip to avoid extracting the zipfile.
- partial_target_file = common.MakeTempFile(suffix='.zip')
- cmd = ['zip2zip', '-i', input_file, '-o', partial_target_file]
- cmd.extend(['{}:{}'.format(name, name) for name in copy_entries])
- common.RunAndCheckOutput(cmd)
+ for info_file in ['META/misc_info.txt', DYNAMIC_PARTITION_INFO]:
+ if not DoesInputFileContain(input_file, info_file):
+ logger.warning('Cannot find %s in input zipfile', info_file)
+ continue
- partial_target_zip = zipfile.ZipFile(partial_target_file, 'a',
- allowZip64=True)
- with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
- common.ZipWriteStr(partial_target_zip, 'META/ab_partitions.txt',
- '\n'.join(ab_partitions))
- CARE_MAP_ENTRY = "META/care_map.pb"
- if CARE_MAP_ENTRY in input_zip.namelist():
- caremap = care_map_pb2.CareMap()
- caremap.ParseFromString(input_zip.read(CARE_MAP_ENTRY))
- filtered = [
- part for part in caremap.partitions if part.name in ab_partitions]
- del caremap.partitions[:]
- caremap.partitions.extend(filtered)
- common.ZipWriteStr(partial_target_zip, CARE_MAP_ENTRY,
- caremap.SerializeToString())
+ content = common.ReadFromInputFile(input_file, info_file)
+ modified_info = UpdatesInfoForSpecialUpdates(
+ content, lambda p: p in ab_partitions)
+ if OPTIONS.vabc_compression_param and info_file == DYNAMIC_PARTITION_INFO:
+ modified_info = ModifyVABCCompressionParam(
+ modified_info, OPTIONS.vabc_compression_param)
+ common.WriteToInputFile(input_file, info_file, modified_info)
- for info_file in ['META/misc_info.txt', DYNAMIC_PARTITION_INFO]:
- if info_file not in input_zip.namelist():
- logger.warning('Cannot find %s in input zipfile', info_file)
- continue
- content = input_zip.read(info_file).decode()
- modified_info = UpdatesInfoForSpecialUpdates(
- content, lambda p: p in ab_partitions)
- if OPTIONS.vabc_compression_param and info_file == DYNAMIC_PARTITION_INFO:
- modified_info = ModifyVABCCompressionParam(
- modified_info, OPTIONS.vabc_compression_param)
- common.ZipWriteStr(partial_target_zip, info_file, modified_info)
+ def IsInPartialList(postinstall_line: str):
+ idx = postinstall_line.find("=")
+ if idx < 0:
+ return False
+ key = postinstall_line[:idx]
+ logger.info("%s %s", key, ab_partitions)
+ for part in ab_partitions:
+ if key.endswith("_" + part):
+ return True
+ return False
- # TODO(xunchang) handle META/postinstall_config.txt'
+ postinstall_config = common.ReadFromInputFile(input_file, POSTINSTALL_CONFIG)
+ postinstall_config = [
+ line for line in postinstall_config.splitlines() if IsInPartialList(line)]
+ if postinstall_config:
+ postinstall_config = "\n".join(postinstall_config)
+ common.WriteToInputFile(input_file, POSTINSTALL_CONFIG, postinstall_config)
+ else:
+ os.unlink(os.path.join(input_file, POSTINSTALL_CONFIG))
- common.ZipClose(partial_target_zip)
-
- return partial_target_file
+ return input_file
def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
@@ -664,21 +667,12 @@
replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
for dev in super_block_devices}
- target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
- shutil.copyfile(input_file, target_file)
-
- with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
- namelist = input_zip.namelist()
-
- input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
-
# Remove partitions from META/ab_partitions.txt that is in
# dynamic_partition_list but not in super_block_devices so that
# brillo_update_payload won't generate update for those logical partitions.
- ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
- with open(ab_partitions_file) as f:
- ab_partitions_lines = f.readlines()
- ab_partitions = [line.strip() for line in ab_partitions_lines]
+ ab_partitions_lines = common.ReadFromInputFile(
+ input_file, AB_PARTITIONS).split("\n")
+ ab_partitions = [line.strip() for line in ab_partitions_lines]
# Assert that all super_block_devices are in ab_partitions
super_device_not_updated = [partition for partition in super_block_devices
if partition not in ab_partitions]
@@ -686,15 +680,6 @@
"{} is in super_block_devices but not in {}".format(
super_device_not_updated, AB_PARTITIONS)
# ab_partitions -= (dynamic_partition_list - super_block_devices)
- new_ab_partitions = common.MakeTempFile(
- prefix="ab_partitions", suffix=".txt")
- with open(new_ab_partitions, 'w') as f:
- for partition in ab_partitions:
- if (partition in dynamic_partition_list and
- partition not in super_block_devices):
- logger.info("Dropping %s from ab_partitions.txt", partition)
- continue
- f.write(partition + "\n")
to_delete = [AB_PARTITIONS]
# Always skip postinstall for a retrofit update.
@@ -707,24 +692,28 @@
# Remove the existing partition images as well as the map files.
to_delete += list(replace.values())
to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
-
- common.ZipDelete(target_file, to_delete)
-
- target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
+ for item in to_delete:
+ os.unlink(os.path.join(input_file, item))
# Write super_{foo}.img as {foo}.img.
for src, dst in replace.items():
- assert src in namelist, \
+ assert DoesInputFileContain(input_file, src), \
'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
- unzipped_file = os.path.join(input_tmp, *src.split('/'))
- common.ZipWrite(target_zip, unzipped_file, arcname=dst)
+ source_path = os.path.join(input_file, *src.split("/"))
+ target_path = os.path.join(input_file, *dst.split("/"))
+ os.rename(source_path, target_path)
# Write new ab_partitions.txt file
- common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
+ new_ab_partitions = os.paht.join(input_file, AB_PARTITIONS)
+ with open(new_ab_partitions, 'w') as f:
+ for partition in ab_partitions:
+ if (partition in dynamic_partition_list and
+ partition not in super_block_devices):
+ logger.info("Dropping %s from ab_partitions.txt", partition)
+ continue
+ f.write(partition + "\n")
- common.ZipClose(target_zip)
-
- return target_file
+ return input_file
def GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images):
@@ -833,14 +822,20 @@
return pattern.search(output) is not None
+def ExtractOrCopyTargetFiles(target_file):
+ if os.path.isdir(target_file):
+ return CopyTargetFilesDir(target_file)
+ else:
+ return ExtractTargetFiles(target_file)
+
+
def GenerateAbOtaPackage(target_file, output_file, source_file=None):
"""Generates an Android OTA package that has A/B update payload."""
# If input target_files are directories, create a copy so that we can modify
# them directly
- if os.path.isdir(target_file):
- target_file = CopyTargetFilesDir(target_file)
- if source_file is not None and os.path.isdir(source_file):
- source_file = CopyTargetFilesDir(source_file)
+ target_file = ExtractOrCopyTargetFiles(target_file)
+ if source_file is not None:
+ source_file = ExtractOrCopyTargetFiles(source_file)
# Stage the output zip package for package signing.
if not OPTIONS.no_signing:
staging_file = common.MakeTempFile(suffix='.zip')
@@ -851,7 +846,7 @@
allowZip64=True)
if source_file is not None:
- source_file = ota_utils.ExtractTargetFiles(source_file)
+ source_file = ExtractTargetFiles(source_file)
assert "ab_partitions" in OPTIONS.source_info_dict, \
"META/ab_partitions.txt is required for ab_update."
assert "ab_partitions" in OPTIONS.target_info_dict, \
@@ -948,15 +943,16 @@
elif OPTIONS.partial:
target_file = GetTargetFilesZipForPartialUpdates(target_file,
OPTIONS.partial)
- elif OPTIONS.vabc_compression_param:
+ if OPTIONS.vabc_compression_param:
target_file = GetTargetFilesZipForCustomVABCCompression(
target_file, OPTIONS.vabc_compression_param)
- elif OPTIONS.skip_postinstall:
+ if OPTIONS.skip_postinstall:
target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
# Target_file may have been modified, reparse ab_partitions
target_info.info_dict['ab_partitions'] = common.ReadFromInputFile(target_file,
AB_PARTITIONS).strip().split("\n")
+ from check_target_files_vintf import CheckVintfIfTrebleEnabled
CheckVintfIfTrebleEnabled(target_file, target_info)
# Metadata to comply with Android OTA package format.
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 3291d56..9067e78 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -1047,10 +1047,15 @@
def CopyTargetFilesDir(input_dir):
output_dir = common.MakeTempDir("target_files")
- shutil.copytree(os.path.join(input_dir, "IMAGES"), os.path.join(
- output_dir, "IMAGES"), dirs_exist_ok=True)
+ IMAGES_DIR = ["IMAGES", "PREBUILT_IMAGES", "RADIO"]
+ for subdir in IMAGES_DIR:
+ if not os.path.exists(os.path.join(input_dir, subdir)):
+ continue
+ shutil.copytree(os.path.join(input_dir, subdir), os.path.join(
+ output_dir, subdir), dirs_exist_ok=True, copy_function=os.link)
shutil.copytree(os.path.join(input_dir, "META"), os.path.join(
output_dir, "META"), dirs_exist_ok=True)
+
for (dirpath, _, filenames) in os.walk(input_dir):
for filename in filenames:
path = os.path.join(dirpath, filename)
diff --git a/tools/sbom/generate-sbom.py b/tools/sbom/generate-sbom.py
index 1e98699..2415f7e 100755
--- a/tools/sbom/generate-sbom.py
+++ b/tools/sbom/generate-sbom.py
@@ -263,8 +263,8 @@
def get_sbom_fragments(installed_file_metadata, metadata_file_path):
"""Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT
- package, a UPSTREAM package if it's a source package and a external SBOM document reference if
- it's a prebuilt package with sbom_ref defined in its METADATA file.
+ package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its
+ METADATA file.
See go/android-spdx and go/android-sbom-gen for more details.
"""
@@ -301,25 +301,33 @@
prebuilt_package = sbom_data.Package(id=prebuilt_package_id,
name=name,
download_location=sbom_data.VALUE_NONE,
- version=args.build_version,
+ version=version if version else args.build_version,
supplier='Organization: ' + args.product_mfr)
- packages.append(prebuilt_package)
- if metadata_file_path:
- metadata_proto = metadata_file_protos[metadata_file_path]
- if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':
- sbom_url = metadata_proto.third_party.sbom_ref.url
- sbom_checksum = metadata_proto.third_party.sbom_ref.checksum
- upstream_element_id = metadata_proto.third_party.sbom_ref.element_id
- if sbom_url and sbom_checksum and upstream_element_id:
- doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{encode_for_spdxid(name)}'
- external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,
- uri=sbom_url,
- checksum=sbom_checksum)
- relationships.append(
- sbom_data.Relationship(id1=prebuilt_package_id,
- relationship=sbom_data.RelationshipType.VARIANT_OF,
- id2=doc_ref_id + ':' + upstream_element_id))
+ upstream_package_id = new_package_id(name, PKG_UPSTREAM)
+ upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version = version,
+ supplier=('Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,
+ download_location=download_location)
+ packages += [prebuilt_package, upstream_package]
+ relationships.append(sbom_data.Relationship(id1=prebuilt_package_id,
+ relationship=sbom_data.RelationshipType.VARIANT_OF,
+ id2=upstream_package_id))
+
+ if metadata_file_path:
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':
+ sbom_url = metadata_proto.third_party.sbom_ref.url
+ sbom_checksum = metadata_proto.third_party.sbom_ref.checksum
+ upstream_element_id = metadata_proto.third_party.sbom_ref.element_id
+ if sbom_url and sbom_checksum and upstream_element_id:
+ doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{encode_for_spdxid(name)}'
+ external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,
+ uri=sbom_url,
+ checksum=sbom_checksum)
+ relationships.append(
+ sbom_data.Relationship(id1=upstream_package_id,
+ relationship=sbom_data.RelationshipType.VARIANT_OF,
+ id2=doc_ref_id + ':' + upstream_element_id))
return external_doc_ref, packages, relationships